copyops.tcl

Go to the documentation of this file.
00001 /*  -*- tcl -*-*/
00002 /*  ### ### ### ######### ######### #########*/
00003 /** 
00004  * Basic byte transfer facilities. Essentially fcopy with a different
00005  * API, hopefully better (progress and completion are separate). Also
00006  * equivalent facilities to transfer an explicitly given string instead
00007  * of reading the data to be transfered from a channel.
00008  */
00009 
00010 /*  ### ### ### ######### ######### #########*/
00011 /*  Requirements*/
00012 
00013 package require Tcl 8.4
00014 
00015 namespace ::transfer::copy {
00016     namespace export do chan string options
00017     namespace export doChan doString
00018 }
00019 
00020 /*  ### ### ### ######### ######### #########*/
00021 /*  API commands*/
00022 
00023 ret  ::transfer::copy::do (type type , type in , type out , type args) {
00024     switch -exact -- $type {
00025     chan - string {}
00026     default {
00027         return -code error \
00028             "Unknown type \"$type\",\
00029             expected chan, or string"
00030     }
00031     }
00032 
00033     options $out $args settings
00034 
00035     switch -exact -- $type {
00036     chan   {doChan   $in $out settings}
00037     string {doString $in $out settings}
00038     }
00039     return
00040 }
00041 
00042 ret  ::transfer::copy::chan (type in , type out , type args) {
00043     # Options: -size n
00044     #          -blocksize n
00045     #          -progress cmd, (cmd n)       - feedback
00046     #          -command cmd,  (cmd n ?err?) - completion
00047 
00048     options     $out $args settings
00049     doChan  $in $out       settings
00050     return
00051 }
00052 
00053 ret  ::transfer::copy::string (type string , type out , type args) {
00054     # Options: -size n
00055     #          -blocksize n
00056     #          -progress cmd, (cmd n)       - feedback
00057     #          -command cmd,  (cmd n ?err?) - completion
00058 
00059     options          $out $args settings
00060     doString $string $out       settings
00061     return
00062 }
00063 
00064 ret  ::transfer::copy::options (type chan , type alist , type optv , optional defaults ={)} {
00065     upvar 1 $optv settings
00066 
00067     # Prepare defaults, hardwired, output channel, and caller
00068 
00069     array set settings {
00070     -size     -1
00071     -progress {}
00072     -command  {}
00073     }
00074 
00075     array  settings =  [CGet $chan]
00076     array  settings =  $defaults
00077 
00078     /*  Process the options*/
00079 
00080      capture =  0
00081     foreach o $alist {
00082     /*  Store argument to previous option*/
00083     if {$capture} {
00084          settings = ($key) $o
00085          capture =  0
00086         continue
00087     }
00088     /*  Dispatch & process the option*/
00089     switch -exact -- $o {
00090         -blocksize -
00091         -command -
00092         -encoding -
00093         -eofchar -
00094         -progress -
00095         -size -
00096         -translation {
00097          key =  $o
00098          capture =  1
00099         }
00100         default {
00101         return -code error \
00102             "Unknown option \"$o\",\
00103             expected one of -size,\
00104             -blocksize, -progress,\
00105             or -command"
00106         }
00107     }
00108     }
00109     if {$capture} {
00110     return -code error \
00111         "wrong\/* args, option \"$o\" \*/
00112         is without argument"
00113     }
00114 
00115     if {![llength $tings = (-command)]} {
00116     return -code error \
00117         "Completion callback is missing"
00118     }
00119 
00120     return
00121 }
00122 
00123 /*  ### ### ### ######### ######### #########*/
00124 /*  Implementation. Transfer from a channel.*/
00125 
00126 ret  ::transfer::copy::doChan (type in , type out , type ov) {
00127     upvar 1 $ov settings
00128 
00129     upvar 0 settings(-size)    size
00130     upvar 0 settings(-command) command
00131 
00132     if {$size == 0} {
00133     # Nothing to transfer. Is that an error, or mildy ok ?
00134     # For now: Ok.
00135 
00136     Run command 0
00137     return
00138     }
00139 
00140     set state [CGet $out]
00141     Configure $out [array get settings]
00142     upvar 0 settings(-progress) progress
00143 
00144     if {$size > 0} {
00145     fcopy $in $out -size $size -command \
00146         [list \
00147         ::transfer::copy::HandlerChan \
00148         $size $progress $command \
00149         $in $out $state]
00150     } else {
00151     fcopy $in $out -command [list \
00152         ::transfer::copy::HandlerChan \
00153         $size $progress $command \
00154         $in $out $state]
00155     return
00156     }
00157 }
00158 
00159 ret  ::transfer::copy::HandlerChan (
00160     type size , type progress , type command
00161     , type in , type out , type state
00162     , type transfered , type args
00163 ) {
00164     # Progress
00165     if {[llength $progress]} {
00166     Run progress $transfered
00167     }
00168 
00169     # Error signaled ?
00170     if {[llength $args]} {
00171     # Restore channel state and then propagate the problem
00172     # forward.
00173     Configure $out $state
00174     Run command $transfered [lindex $args 0]
00175     return
00176     }
00177 
00178     # How much transfered, have we transfered everything ?
00179 
00180     if {($size >= 0) && ($size <= $transfered)} {
00181     # Everything has been transfered, trigger completion
00182     # callback. The caller has to close the output channel!
00183 
00184     Configure $out $state
00185     Run command $transfered
00186     return
00187     }
00188 
00189     if {[eof $in]} {
00190     # Input has closed, action depends on the specified size. -1
00191     # signals transfer to eof, so we are now done. Otherwise we
00192     # have transfered less than we wanted, and that is an error.
00193 
00194     Configure $out $state
00195     if {$size < 0} {
00196         Run command $transfered
00197     } else {
00198         Run command $transfered \
00199             "Transfer aborted, not\
00200             enough input"
00201     }
00202     return
00203     }
00204 
00205     # Wait for more callbacks.
00206     return
00207 }
00208 
00209 /*  ### ### ### ######### ######### #########*/
00210 /*  Implementation. Transfer from a string.*/
00211 
00212 ret  ::transfer::copy::doString (type str , type out , type ov) {
00213     upvar 1 $ov settings
00214 
00215     upvar 0 settings(-size)    size
00216     upvar 0 settings(-command) command
00217 
00218     if {$size == 0} {
00219     # Nothing to transfer. Is that an error, or mildy ok ?
00220     # For now: Ok
00221 
00222     Run command 0
00223     return
00224     }
00225 
00226     set length [::string length $str]
00227     if {$size > 0} {
00228     if {$length < $size} {
00229         Run command 0 \
00230             "Transfer impossible,\
00231             not enough data for size"]
00232         return
00233     }
00234     set last $size
00235     } else {
00236     # size < 0 (Note size == 0 already captured)
00237     set last $length
00238     set size $last
00239     }
00240 
00241     # We transfer the string in chunks of -blocksize. We cannot use
00242     # fcopy for this, so do our own event processing.
00243 
00244     set state [CGet $out]
00245     Configure $out [array get settings]
00246 
00247     upvar 0 settings(-blocksize) blocksize
00248     upvar 0 settings(-progress)  progress
00249 
00250     fileevent $out writable [list \
00251         ::transfer::copy::HandlerString \
00252         $size 0 $last \
00253         $blocksize 0 [expr {$blocksize - 1}] \
00254         $progress $command \
00255         $str $out $state]
00256     return
00257 }
00258 
00259 ret  ::transfer::copy::HandlerString (
00260     type pending , type transfered , type last
00261     , type block , type from , type to
00262     , type progress , type command
00263     , type str , type out , type state
00264 ) {
00265     # pending + transfered = last. from/to is chunk to transfer.
00266 
00267     if {$to > $last} {
00268     set  to         end
00269     incr transfered $pending
00270     set  pending    0
00271     }
00272 
00273     set code [catch {
00274     puts -nonewline $out \
00275         [::string range $str $from $to]
00276     } res]
00277     if {$code} {
00278     Configure $out $state
00279     fileevent $out writable {}
00280     Run command $transfered $res
00281     return
00282     }
00283 
00284     if {[llength $progress]} {
00285     Run progress $transfered
00286     }
00287 
00288     if {$pending == 0} {
00289     # Done
00290     Configure $out $state
00291     fileevent $out writable {}
00292     Run command $transfered
00293     }
00294 
00295     # Prepare for next chunk
00296 
00297     incr transfered $block
00298     incr pending   -$block
00299     incr from       $block
00300     incr to         $block
00301 
00302     fileevent $out writable [list \
00303           ::transfer::copy::HandlerString \
00304           $pending $transfered $last \
00305           $block $from $to \
00306           $progress $command \
00307           $str $out $state]
00308     return
00309 }
00310 
00311 /*  ### ### ### ######### ######### #########*/
00312 /*  Implementation. Support commands.*/
00313 
00314 ret  ::transfer::copy::Run (type cmdv , type args) {
00315     upvar 1 $cmdv c
00316     set command $c
00317     foreach a $args {lappend command $a}
00318     return [uplevel #0 $command]
00319 
00320     # 8.5: {*}$c {*}$args
00321 }
00322 
00323 ret  ::transfer::copy::CGet (type chan) {
00324     array set settings {}
00325 
00326     foreach o {
00327     -buffersize -encoding -translation -eofchar -blocking
00328     } {
00329     set settings($o) [fconfigure $chan $o]
00330     }
00331 
00332     set   settings(-blocksize) $settings(-buffersize)
00333     unset settings(-buffersize)
00334     return [array get settings]
00335 }
00336 
00337 ret  ::transfer::copy::Configure (type chan , type settings) {
00338     array set tmp $settings
00339 
00340     set   tmp(-buffersize) $tmp(-blocksize)
00341     unset tmp(-blocksize)
00342     unset -nocomplain tmp(-progress)
00343     unset -nocomplain tmp(-command)
00344     unset -nocomplain tmp(-size)
00345 
00346     foreach o [array names tmp] {
00347     fconfigure $chan $o $tmp($o)
00348     }
00349     return
00350 }
00351 
00352 /*  ### ### ### ######### ######### #########*/
00353 /*  Ready*/
00354 
00355 package provide transfer::copy 0.1
00356 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1