00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 
00025 
00026 
00027 package require Tcl 8.3
00028 package require snit ; 
00029 
00030 namespace ::comm {
00031     namespace export comm comm_send
00032 
00033     variable  comm
00034     array  comm =  {}
00035 
00036     if {![info exists comm(chans)]} {
00037     array  comm =  {
00038         debug 0 chans {} localhost 127.0.0.1
00039         connecting,hook 1
00040         connected,hook  1
00041         incoming,hook   1
00042         eval,hook       1
00043         callback,hook   1
00044         reply,hook      1
00045         lost,hook       1
00046         offerVers       {3 2}
00047         acceptVers      {3 2}
00048         defVers     2
00049         defaultEncoding "utf-8"
00050         defaultSilent   0
00051     }
00052      comm = (lastport) [expr {[pid] % 32768 + 9999}]
00053     
00054     foreach comm(_x) $comm(acceptVers) {
00055          comm = ($comm(_x),vers) 1
00056     }
00057     catch {un comm = (_x)}
00058     }
00059 
00060     
00061     
00062     
00063     
00064     
00065     
00066     
00067     
00068     
00069     
00070     
00071     
00072     
00073     
00074     
00075     
00076     
00077     
00078     
00079     
00080     
00081     
00082     
00083     
00084     
00085     
00086     
00087     
00088     
00089 
00090     if {0} {
00091     
00092     
00093     global errorInfo errorCode
00094      code =  [catch [concat commSend $args] res]
00095     return -code $code -errorinfo $errorInfo -errorcode $errorCode $res
00096     }
00097 }
00098 
00099 
00100 
00101 
00102 
00103 
00104 
00105 
00106 
00107 
00108 
00109 
00110 
00111 ret  ::comm::comm_send () {
00112     proc send {args} {
00113     # Use pure lists to speed this up.
00114     uplevel 1 [linsert $args 0 ::comm::comm send]
00115     }
00116     rename winfo tk_winfo
00117     proc winfo {cmd args} {
00118     if {![string match in* $cmd]} {
00119         # Use pure lists to speed this up ...
00120         return [uplevel 1 [linsert $args 0 tk_winfo $cmd]]
00121     }
00122     return [::comm::comm interps]
00123     }
00124     proc ::comm::comm_send {} {}
00125 }
00126 
00127 
00128 
00129 
00130 
00131 
00132 
00133 
00134 
00135 
00136 
00137 
00138 
00139 
00140 ret  ::comm::comm (type cmd , type args) {
00141     set method [info commands ::comm::comm_cmd_$cmd*]
00142 
00143     if {[llength $method] == 1} {
00144     set chan ::comm::comm; # passed to methods
00145     return [uplevel 1 [linsert $args 0 $method $chan]]
00146     } else {
00147     foreach c [info commands ::comm::comm_cmd_*] {
00148         # remove ::comm::comm_cmd_
00149         lappend cmds [string range $c 17 end]
00150     }
00151         return -code error "unknown subcommand \"$cmd\":\
00152         must be one of [join [lsort $cmds] {, }]"
00153     }
00154 }
00155 
00156 ret  ::comm::comm_cmd_connect (type chan , type args) {
00157     uplevel 1 [linsert $args 0 [namespace current]::commConnect $chan]
00158 }
00159 ret  ::comm::comm_cmd_self (type chan , type args) {
00160     variable comm
00161     return $comm($chan,port)
00162 }
00163 ret  ::comm::comm_cmd_channels (type chan , type args) {
00164     variable comm
00165     return $comm(chans)
00166 }
00167 ret  ::comm::comm_cmd_configure (type chan , type args) {
00168     uplevel 1 [linsert $args 0 [namespace current]::commConfigure $chan 0]
00169 }
00170 ret  ::comm::comm_cmd_ids (type chan , type args) {
00171     variable comm
00172     set res $comm($chan,port)
00173     foreach {i id} [array get comm $chan,fids,*] {lappend res $id}
00174     return $res
00175 }
00176 interp alias {} ::comm::comm_cmd_interps {} ::comm::comm_cmd_ids
00177 ret  ::comm::comm_cmd_remoteid (type chan , type args) {
00178     variable comm
00179     if {[info exists comm($chan,remoteid)]} {
00180     set comm($chan,remoteid)
00181     } else {
00182     return -code error "No remote commands processed yet"
00183     }
00184 }
00185 ret  ::comm::comm_cmd_debug (type chan , type bool) {
00186     variable comm
00187     return [set comm(debug) [string is true -strict $bool]]
00188 }
00189 
00190 
00191 
00192 
00193 
00194 
00195 
00196 
00197 ret  ::comm::comm_cmd_return_async (type chan) {
00198     variable comm
00199 
00200     if {![info exists comm(current,async)]} {
00201     return -code error "No remote commands processed yet"
00202     }
00203     if {$comm(current,async)} {
00204     # Return the same future which were generated by the first
00205     # call.
00206     return $comm(current,state)
00207     }
00208 
00209     foreach {cmdchan cmdfid cmd ser} $comm(current,state) break
00210 
00211     # Assert that the channel performing the request and the channel
00212     # the current command came in are identical. Panic if not.
00213 
00214     if {![string equal $chan $cmdchan]} {
00215     return -code error "Internal error: Trying to activate\
00216         async return for a command on a different channel"
00217     }
00218 
00219     # Establish the future for the command and return a handle for
00220     # it. Remember the outstanding futures for a peer, so that we can
00221     # cancel them if the peer is lost before the promise implicit in
00222     # the future is redeemed.
00223 
00224     set future [::comm::future %AUTO% $chan $cmdfid $cmd $ser]
00225 
00226     lappend comm(future,fid,$cmdfid) $future
00227     set     comm(current,state)      $future
00228 
00229     # Mark the current command as using async result return. We do
00230     # this last to ensure that all errors in this method are reported
00231     # through the regular channels.
00232 
00233     set comm(current,async) 1
00234 
00235     return $future
00236 }
00237 
00238 
00239 
00240 
00241 
00242 
00243 
00244 
00245 
00246 
00247 
00248 
00249 ret  ::comm::comm_cmd_hook (type chan , type hook , optional script =+) {
00250     variable comm
00251     if {![info exists comm($hook,hook)]} {
00252     return -code error "Unknown hook invoked"
00253     }
00254     if {!$comm($hook,hook)} {
00255     return -code error "Unimplemented hook invoked"
00256     }
00257     if {[string equal + $script]} {
00258     if {[catch {set comm($chan,hook,$hook)} ret]} {
00259         return
00260     }
00261     return $ret
00262     }
00263     if {[string match +* $script]} {
00264     append comm($chan,hook,$hook) \n [string range $script 1 end]
00265     } else {
00266     set comm($chan,hook,$hook) $script
00267     }
00268     return
00269 }
00270 
00271 
00272 
00273 
00274 
00275 
00276 
00277 
00278 
00279 
00280 
00281 
00282 ret  ::comm::comm_cmd_abort (type chan) {
00283     variable comm
00284 
00285     foreach pid [array names comm $chan,peers,*] {
00286     commLostConn $chan $comm($pid) "Connection aborted by request"
00287     }
00288 }
00289 
00290 
00291 
00292 
00293 
00294 
00295 
00296 
00297 
00298 
00299 
00300 
00301 ret  ::comm::comm_cmd_destroy (type chan) {
00302     variable comm
00303     catch {close $comm($chan,socket)}
00304     comm_cmd_abort $chan
00305     if {$comm($chan,interp) != {}} {
00306     interp delete $comm($chan,interp)
00307     }
00308     catch {unset comm($chan,port)}
00309     catch {unset comm($chan,local)}
00310     catch {unset comm($chan,silent)}
00311     catch {unset comm($chan,interp)}
00312     catch {unset comm($chan,events)}
00313     catch {unset comm($chan,socket)}
00314     catch {unset comm($chan,remoteid)}
00315     unset comm($chan,serial)
00316     unset comm($chan,chan)
00317     unset comm($chan,encoding)
00318     unset comm($chan,listen)
00319     # array unset would have been nicer, but is not available in
00320     # 8.2/8.3
00321     foreach pattern {hook,* interp,* vers,*} {
00322     foreach k [array names comm $chan,$pattern] {unset comm($k)}
00323     }
00324     set pos [lsearch -exact $comm(chans) $chan]
00325     set comm(chans) [lreplace $comm(chans) $pos $pos]
00326     if {![string equal ::comm::comm $chan]} {
00327     rename $chan {}
00328     }
00329     return
00330 }
00331 
00332 
00333 
00334 
00335 
00336 
00337 
00338 
00339 
00340 
00341 
00342 
00343 ret  ::comm::comm_cmd_shutdown (type chan , type id) {
00344     variable comm
00345 
00346     if {[info exists comm($chan,peers,$id)]} {
00347     commLostConn $chan $comm($chan,peers,$id) \
00348         "Connection shutdown by request"
00349     }
00350 }
00351 
00352 
00353 
00354 
00355 
00356 
00357 
00358 
00359 
00360 
00361 
00362 
00363 
00364 ret  ::comm::comm_cmd_new (type chan , type ch , type args) {
00365     variable comm
00366 
00367     if {[lsearch -exact $comm(chans) $ch] >= 0} {
00368     return -code error "Already existing channel: $ch"
00369     }
00370     if {([llength $args] % 2) != 0} {
00371     return -code error "Must have an even number of config arguments"
00372     }
00373     # ensure that the new channel name is fully qualified
00374     set ch ::[string trimleft $ch :]
00375     if {[string equal ::comm::comm $ch]} {
00376     # allow comm to be recreated after destroy
00377     } elseif {[string equal $ch [info commands $ch]]} {
00378     return -code error "Already existing command: $ch"
00379     } else {
00380     # Create the new channel with fully qualified proc name
00381     proc $ch {cmd args} {
00382         set method [info commands ::comm::comm_cmd_$cmd*]
00383 
00384         if {[llength $method] == 1} {
00385         # this should work right even if aliased
00386         # it is passed to methods to identify itself
00387         set chan [namespace origin [lindex [info level 0] 0]]
00388         return [uplevel 1 [linsert $args 0 $method $chan]]
00389         } else {
00390         foreach c [info commands ::comm::comm_cmd_*] {
00391             # remove ::comm::comm_cmd_
00392             lappend cmds [string range $c 17 end]
00393         }
00394         return -code error "unknown subcommand \"$cmd\":\
00395             must be one of [join [lsort $cmds] {, }]"
00396         }
00397     }
00398     }
00399     lappend comm(chans) $ch
00400     set chan $ch
00401     set comm($chan,serial) 0
00402     set comm($chan,chan)   $chan
00403     set comm($chan,port)   0
00404     set comm($chan,listen) 0
00405     set comm($chan,socket) ""
00406     set comm($chan,local)  1
00407     set comm($chan,silent)   $comm(defaultSilent)
00408     set comm($chan,encoding) $comm(defaultEncoding)
00409     set comm($chan,interp)   {}
00410     set comm($chan,events)   {}
00411 
00412     if {[llength $args] > 0} {
00413     if {[catch [linsert $args 0 commConfigure $chan 1] err]} {
00414         comm_cmd_destroy $chan
00415         return -code error $err
00416     }
00417     }
00418     return $chan
00419 }
00420 
00421 
00422 
00423 
00424 
00425 
00426 
00427 
00428 
00429 
00430 
00431 
00432 ret  ::comm::comm_cmd_send (type chan , type args) {
00433     variable comm
00434 
00435     set cmd send
00436 
00437     # args = ?-async | -command command? id cmd ?arg arg ...?
00438     set i 0
00439     set opt [lindex $args $i]
00440     if {[string equal -async $opt]} {
00441     set cmd async
00442     incr i
00443     } elseif {[string equal -command $opt]} {
00444     set cmd command
00445     set callback [lindex $args [incr i]]
00446     incr i
00447     }
00448     # args = id cmd ?arg arg ...?
00449 
00450     set id [lindex $args $i]
00451     incr i
00452     set args [lrange $args $i end]
00453 
00454     if {![info complete $args]} {
00455     return -code error "Incomplete command"
00456     }
00457     if {![llength $args]} {
00458     return -code error \
00459         "wrong # args: should be \"send ?-async? id arg ?arg ...?\""
00460     }
00461     if {[catch {commConnect $chan $id} fid]} {
00462     return -code error "Connect to remote failed: $fid"
00463     }
00464 
00465     set ser [incr comm($chan,serial)]
00466     # This is unneeded - wraps from 2147483647 to -2147483648
00467     ### if {$comm($chan,serial) == 0x7fffffff} {set comm($chan,serial) 0}
00468 
00469     commDebug {puts stderr "<$chan> send <[list [list $cmd $ser $args]]>"}
00470 
00471     # The double list assures that the command is a single list when read.
00472     puts  $fid [list [list $cmd $ser $args]]
00473     flush $fid
00474 
00475     commDebug {puts stderr "<$chan> sent"}
00476 
00477     # wait for reply if so requested
00478 
00479     if {[string equal command $cmd]} {
00480     # In this case, don't wait on the command result.  Set the callback
00481     # in the return and that will be invoked by the result.
00482     lappend comm($chan,pending,$id) [list $ser callback]
00483     set comm($chan,return,$ser) $callback
00484     return $ser
00485     } elseif {[string equal send $cmd]} {
00486     upvar 0 comm($chan,pending,$id) pending ;# shorter variable name
00487 
00488     lappend pending $ser
00489     set comm($chan,return,$ser) ""      ;# we're waiting
00490 
00491     commDebug {puts stderr "<$chan> --<<waiting $ser>>--"}
00492     vwait ::comm::comm($chan,result,$ser)
00493 
00494     # if connection was lost, pending is gone
00495     if {[info exists pending]} {
00496         set pos [lsearch -exact $pending $ser]
00497         set pending [lreplace $pending $pos $pos]
00498     }
00499 
00500     commDebug {
00501         puts stderr "<$chan> result\
00502             <$comm($chan,return,$ser);$comm($chan,result,$ser)>"
00503     }
00504 
00505     array set return $comm($chan,return,$ser)
00506     unset comm($chan,return,$ser)
00507     set thisres $comm($chan,result,$ser)
00508     unset comm($chan,result,$ser)
00509     switch -- $return(-code) {
00510         "" - 0 {return $thisres}
00511         1 {
00512         return  -code $return(-code) \
00513             -errorinfo $return(-errorinfo) \
00514             -errorcode $return(-errorcode) \
00515             $thisres
00516         }
00517         default {return -code $return(-code) $thisres}
00518     }
00519     }
00520 }
00521 
00522 /* */
00523 
00524 /*  ::comm::commDebug --*/
00525 /* */
00526 /*  Internal command. Conditionally executes debugging*/
00527 /*  statements. Currently this are only puts commands logging the*/
00528 /*  various interactions. These could be replaced with calls into*/
00529 /*  the 'log' module.*/
00530 /* */
00531 /*  Arguments:*/
00532 /*  arg Tcl script to execute.*/
00533 /* */
00534 /*  Results:*/
00535 /*  None.*/
00536 
00537 ret  ::comm::commDebug (type cmd) {
00538     variable comm
00539     if {$comm(debug)} {
00540     uplevel 1 $cmd
00541     }
00542 }
00543 
00544 /*  ::comm::commConfVars --*/
00545 /* */
00546 /*  Internal command. Used to declare configuration options.*/
00547 /* */
00548 /*  Arguments:*/
00549 /*  v   Name of configuration option.*/
00550 /*  t   Default value.*/
00551 /* */
00552 /*  Results:*/
00553 /*  None.*/
00554 
00555 ret  ::comm::commConfVars (type v , type t) {
00556     variable comm
00557     set comm($v,var) $t
00558     set comm(vars) {}
00559     foreach c [array names comm *,var] {
00560     lappend comm(vars) [lindex [split $c ,] 0]
00561     }
00562     return
00563 }
00564 ::comm::commConfVars port     p
00565 ::comm::commConfVars local    b
00566 ::comm::commConfVars listen   b
00567 ::comm::commConfVars socket   ro
00568 ::comm::commConfVars chan     ro
00569 ::comm::commConfVars serial   ro
00570 ::comm::commConfVars encoding enc
00571 ::comm::commConfVars silent   b
00572 ::comm::commConfVars interp   interp
00573 ::comm::commConfVars events   ev
00574 
00575 /*  ::comm::commConfigure --*/
00576 /* */
00577 /*  Internal command. Implements 'comm configure'.*/
00578 /* */
00579 /*  Arguments:*/
00580 /*  force   Boolean flag. If set the socket is reinitialized.*/
00581 /*  args    New configuration, as -option value pairs.*/
00582 /* */
00583 /*  Results:*/
00584 /*  None.*/
00585 
00586 ret  ::comm::commConfigure (type chan , optional force =0 , type args) {
00587     variable comm
00588 
00589     # query
00590     if {[llength $args] == 0} {
00591     foreach v $comm(vars) {lappend res -$v $comm($chan,$v)}
00592     return $res
00593     } elseif {[llength $args] == 1} {
00594     set arg [lindex $args 0]
00595     set var [string range $arg 1 end]
00596     if {![string match -* $arg] || ![info exists comm($var,var)]} {
00597         return -code error "Unknown configuration option: $arg"
00598     }
00599     return $comm($chan,$var)
00600     }
00601 
00602     # set
00603     set opt 0
00604     foreach arg $args {
00605     incr opt
00606     if {[info exists skip]} {unset skip; continue}
00607     set var [string range $arg 1 end]
00608     if {![string match -* $arg] || ![info exists comm($var,var)]} {
00609         return -code error "Unknown configuration option: $arg"
00610     }
00611     set optval [lindex $args $opt]
00612     switch $comm($var,var) {
00613         ev {
00614         if {![string equal  $optval ""]} {
00615             set err 0
00616             if {[catch {
00617             foreach ev $optval {
00618                 if {[lsearch -exact {connecting connected incoming eval callback reply lost} $ev] < 0} {
00619                 set err 1
00620                 break
00621                 }
00622             }
00623             }]} {
00624             set err 1
00625             }
00626             if {$err} {
00627             return -code error \
00628                 "Non-event to configuration option: -$var"
00629             }
00630         }
00631         # FRINK: nocheck
00632         set $var $optval
00633         set skip 1
00634         }
00635         interp {
00636         if {
00637             ![string equal  $optval ""] &&
00638             ![interp exists $optval]
00639         } {
00640             return -code error \
00641                 "Non-interpreter to configuration option: -$var"
00642         }
00643         # FRINK: nocheck
00644         set $var $optval
00645         set skip 1
00646         }
00647         b {
00648         # FRINK: nocheck
00649         set $var [string is true -strict $optval]
00650         set skip 1
00651         }
00652         v {
00653         # FRINK: nocheck
00654         set $var $optval
00655         set skip 1
00656         }
00657         p {
00658         if {
00659             ![string equal $optval ""] &&
00660             ![string is integer $optval]
00661         } {
00662             return -code error \
00663             "Non-port to configuration option: -$var"
00664         }
00665         # FRINK: nocheck
00666         set $var $optval
00667         set skip 1
00668         }
00669         i {
00670         if {![string is integer $optval]} {
00671             return -code error \
00672             "Non-integer to configuration option: -$var"
00673         }
00674         # FRINK: nocheck
00675         set $var $optval
00676         set skip 1
00677         }
00678         enc {
00679         # to configure encodings, we will need to extend the
00680         # protocol to allow for handshaked encoding changes
00681         return -code error "encoding not configurable"
00682         if {[lsearch -exact [encoding names] $optval] == -1} {
00683             return -code error \
00684             "Unknown encoding to configuration option: -$var"
00685         }
00686         set $var $optval
00687         set skip 1
00688         }
00689         ro { return -code error "Readonly configuration option: -$var" }
00690     }
00691     }
00692     if {[info exists skip]} {
00693     return -code error "Missing value for option: $arg"
00694     }
00695 
00696     foreach var {port listen local} {
00697     # FRINK: nocheck
00698     if {[info exists $var] && [set $var] != $comm($chan,$var)} {
00699         incr force
00700         # FRINK: nocheck
00701         set comm($chan,$var) [set $var]
00702     }
00703     }
00704 
00705     foreach var {silent interp events} {
00706     # FRINK: nocheck
00707     if {[info exists $var] && ([set $var] != $comm($chan,$var))} {
00708         # FRINK: nocheck
00709         set comm($chan,$var) [set ip [set $var]]
00710         if {[string equal $var "interp"] && ($ip != "")} {
00711         # Interrogate the interp about its capabilities.
00712         #
00713         # Like: set, array set, uplevel present ?
00714         # Or:   The above, hidden ?
00715         #
00716         # This is needed to decide how to execute hook scripts
00717         # and regular scripts in this interpreter.
00718         set comm($chan,interp,set)  [Capability $ip set]
00719         set comm($chan,interp,aset) [Capability $ip array]
00720         set comm($chan,interp,upl)  [Capability $ip uplevel]
00721         }
00722     }
00723     }
00724 
00725     if {[info exists encoding] &&
00726     ![string equal $encoding $comm($chan,encoding)]} {
00727     # This should not be entered yet
00728     set comm($chan,encoding) $encoding
00729     fconfigure $comm($chan,socket) -encoding $encoding
00730     foreach {i sock} [array get comm $chan,peers,*] {
00731         fconfigure $sock -encoding $encoding
00732     }
00733     }
00734 
00735     # do not re-init socket
00736     if {!$force} {return ""}
00737 
00738     # User is recycling object, possibly to change from local to !local
00739     if {[info exists comm($chan,socket)]} {
00740     comm_cmd_abort $chan
00741     catch {close $comm($chan,socket)}
00742     unset comm($chan,socket)
00743     }
00744 
00745     set comm($chan,socket) ""
00746     if {!$comm($chan,listen)} {
00747     set comm($chan,port) 0
00748     return ""
00749     }
00750 
00751     if {[info exists port] && [string equal "" $comm($chan,port)]} {
00752     set nport [incr comm(lastport)]
00753     } else {
00754     set userport 1
00755     set nport $comm($chan,port)
00756     }
00757     while {1} {
00758     set cmd [list socket -server [list ::comm::commIncoming $chan]]
00759     if {$comm($chan,local)} {
00760         lappend cmd -myaddr $comm(localhost)
00761     }
00762     lappend cmd $nport
00763     if {![catch $cmd ret]} {
00764         break
00765     }
00766     if {[info exists userport] || ![string match "*already in use" $ret]} {
00767         # don't eradicate the class
00768         if {![string equal ::comm::comm $chan]} {
00769         rename $chan {}
00770         }
00771         return -code error $ret
00772     }
00773     set nport [incr comm(lastport)]
00774     }
00775     set comm($chan,socket) $ret
00776     fconfigure $ret -translation lf -encoding $comm($chan,encoding)
00777 
00778     # If port was 0, system allocated it for us
00779     set comm($chan,port) [lindex [fconfigure $ret -sockname] 2]
00780     return ""
00781 }
00782 
00783 
00784 
00785 
00786 
00787 
00788 
00789 ret  ::comm::Capability (type interp , type cmd) {
00790     if {[lsearch -exact [interp hidden $interp] $cmd] >= 0} {
00791     # The command is present, although hidden.
00792     return hidden
00793     }
00794 
00795     # The command is not a hidden command. Use info to determine if it
00796     # is present as regular command. Note that the 'info' command
00797     # itself might be hidden.
00798 
00799     if {[catch {
00800     set has [llength [interp eval $interp [list info commands $cmd]]]
00801     }] && [catch {
00802     set has [llength [interp invokehidden $interp info commands $cmd]]
00803     }]} {
00804     # Unable to interogate the interpreter in any way. Assume that
00805     # the command is not present.
00806     set has 0
00807     }
00808     return [expr {$has ? "ok" : "no"}]
00809 }
00810 
00811 
00812 
00813 
00814 
00815 
00816 
00817 
00818 
00819 
00820 
00821 
00822 
00823 
00824 ret  ::comm::commConnect (type chan , type id) {
00825     variable comm
00826 
00827     commDebug {puts stderr "<$chan> commConnect $id"}
00828 
00829     # process connecting hook now
00830     CommRunHook $chan connecting
00831 
00832     if {[info exists comm($chan,peers,$id)]} {
00833     return $comm($chan,peers,$id)
00834     }
00835     if {[lindex $id 0] == 0} {
00836     return -code error "Remote comm is anonymous; cannot connect"
00837     }
00838 
00839     if {[llength $id] > 1} {
00840     set host [lindex $id 1]
00841     } else {
00842     set host $comm(localhost)
00843     }
00844     set port [lindex $id 0]
00845     set fid [socket $host $port]
00846 
00847     # process connected hook now
00848     if {[catch {
00849     CommRunHook $chan connected
00850     } err]} {
00851     global  errorInfo
00852     set ei $errorInfo
00853     close $fid
00854     error $err $ei
00855     }
00856 
00857     # commit new connection
00858     commNewConn $chan $id $fid
00859 
00860     # send offered protocols versions and id to identify ourselves to remote
00861     puts $fid [list $comm(offerVers) $comm($chan,port)]
00862     set comm($chan,vers,$id) $comm(defVers)     ;# default proto vers
00863     flush  $fid
00864     return $fid
00865 }
00866 
00867 
00868 
00869 
00870 
00871 
00872 
00873 
00874 
00875 
00876 
00877 
00878 
00879 
00880 
00881 ret  ::comm::commIncoming (type chan , type fid , type addr , type remport) {
00882     variable comm
00883 
00884     commDebug {puts stderr "<$chan> commIncoming $fid $addr $remport"}
00885 
00886     # process incoming hook now
00887     if {[catch {
00888     CommRunHook $chan incoming
00889     } err]} {
00890     global errorInfo
00891     set ei $errorInfo
00892     close $fid
00893     error $err $ei
00894     }
00895 
00896     # a list of offered proto versions is the first word of first line
00897     # remote id is the second word of first line
00898     # rest of first line is ignored
00899     set protoline   [gets $fid]
00900     set offeredvers [lindex $protoline 0]
00901     set remid       [lindex $protoline 1]
00902 
00903     commDebug {puts stderr "<$chan> offered <$protoline>"}
00904 
00905     # use the first supported version in the offered list
00906     foreach v $offeredvers {
00907     if {[info exists comm($v,vers)]} {
00908         set vers $v
00909         break
00910     }
00911     }
00912     if {![info exists vers]} {
00913     close $fid
00914     if {[info exists comm($chan,silent)] && 
00915         [string is true -strict $comm($chan,silent)]} then return
00916     error "Unknown offered protocols \"$protoline\" from $addr/$remport"
00917     }
00918 
00919     # If the remote host addr isn't our local host addr,
00920     # then add it to the remote id.
00921     if {[string equal [lindex [fconfigure $fid -sockname] 0] $addr]} {
00922     set id $remid
00923     } else {
00924     set id [list $remid $addr]
00925     }
00926 
00927     # Detect race condition of two comms connecting to each other
00928     # simultaneously.  It is OK when we are talking to ourselves.
00929 
00930     if {[info exists comm($chan,peers,$id)] && $id != $comm($chan,port)} {
00931 
00932     puts stderr "commIncoming race condition: $id"
00933     puts stderr "peers=$comm($chan,peers,$id) port=$comm($chan,port)"
00934 
00935     # To avoid the race, we really want to terminate one connection.
00936     # However, both sides are committed to using it.
00937     # commConnect needs to be synchronous and detect the close.
00938     # close $fid
00939     # return $comm($chan,peers,$id)
00940     }
00941 
00942     # Make a protocol response.  Avoid any temptation to use {$vers > 2}
00943     # - this forces forwards compatibility issues on protocol versions
00944     # that haven't been invented yet.  DON'T DO IT!  Instead, test for
00945     # each supported version explicitly.  I.e., {$vers >2 && $vers < 5} is OK.
00946 
00947     switch $vers {
00948     3 {
00949         # Respond with the selected version number
00950         puts  $fid [list [list vers $vers]]
00951         flush $fid
00952     }
00953     }
00954 
00955     # commit new connection
00956     commNewConn $chan $id $fid
00957     set comm($chan,vers,$id) $vers
00958 }
00959 
00960 
00961 
00962 
00963 
00964 
00965 
00966 
00967 
00968 
00969 
00970 
00971 ret  ::comm::commNewConn (type chan , type id , type fid) {
00972     variable comm
00973 
00974     commDebug {puts stderr "<$chan> commNewConn $id $fid"}
00975 
00976     # There can be a race condition two where comms connect to each other
00977     # simultaneously.  This code favors our outgoing connection.
00978 
00979     if {[info exists comm($chan,peers,$id)]} {
00980     # abort this connection, use the existing one
00981     # close $fid
00982     # return -code return $comm($chan,peers,$id)
00983     } else {
00984     set comm($chan,pending,$id) {}
00985         set comm($chan,peers,$id) $fid
00986     }
00987     set comm($chan,fids,$fid) $id
00988     fconfigure $fid -translation lf -encoding $comm($chan,encoding) -blocking 0
00989     fileevent $fid readable [list ::comm::commCollect $chan $fid]
00990 }
00991 
00992 
00993 
00994 
00995 
00996 
00997 
00998 
00999 
01000 
01001 
01002 
01003 
01004 
01005 ret  ::comm::commLostConn (type chan , type fid , type reason) {
01006     variable comm
01007 
01008     commDebug {puts stderr "<$chan> commLostConn $fid $reason"}
01009 
01010     catch {close $fid}
01011 
01012     set id $comm($chan,fids,$fid)
01013 
01014     # Invoke the callbacks of all commands which have such and are
01015     # still waiting for a response from the lost peer. Use an
01016     # appropriate error.
01017 
01018     foreach s $comm($chan,pending,$id) {
01019     if {[string equal "callback" [lindex $s end]]} {
01020         set ser [lindex $s 0]
01021         if {[info exists comm($chan,return,$ser)]} {
01022         set args [list -id       $id \
01023                   -serial    $ser \
01024                   -chan      $chan \
01025                   -code      -1 \
01026                   -errorcode NONE \
01027                   -errorinfo "" \
01028                   -result    $reason \
01029                  ]
01030         if {[catch {uplevel \#0 $comm($chan,return,$ser) $args} err]} {
01031             commBgerror $err
01032         }
01033         }
01034     } else {
01035         set comm($chan,return,$s) {-code error}
01036         set comm($chan,result,$s) $reason
01037     }
01038     }
01039     unset comm($chan,pending,$id)
01040     unset comm($chan,fids,$fid)
01041     catch {unset comm($chan,peers,$id)}     ;# race condition
01042     catch {unset comm($chan,buf,$fid)}
01043 
01044     # Cancel all outstanding futures for requests which were made by
01045     # the lost peer, if there are any. This does not destroy
01046     # them. They will stay around until the long-running operations
01047     # they belong too kill them.
01048 
01049     CancelFutures $fid
01050 
01051     # process lost hook now
01052     catch {CommRunHook $chan lost}
01053 
01054     return $reason
01055 }
01056 
01057 ret  ::comm::commBgerror (type err) {
01058     # SF Tcllib Patch #526499
01059     # (See http://sourceforge.net/tracker/?func=detail&aid=526499&group_id=12883&atid=312883
01060     #  for initial request and comments)
01061     #
01062     # Error in async call. Look for [bgerror] to report it. Same
01063     # logic as in Tcl itself. Errors thrown by bgerror itself get
01064     # reported to stderr.
01065     if {[catch {bgerror $err} msg]} {
01066     puts stderr "bgerror failed to handle background error."
01067     puts stderr "    Original error: $err"
01068     puts stderr "    Error in bgerror: $msg"
01069     flush stderr
01070     }
01071 }
01072 
01073 
01074 
01075 
01076 
01077 
01078 ret  ::comm::CancelFutures (type fid) {
01079     variable comm
01080     if {![info exists comm(future,fid,$fid)]} return
01081 
01082     commDebug {puts stderr "\tCanceling futures: [join $comm(future,fid,$fid) \
01083                          "\n\t                 : "]"}
01084 
01085     foreach future $comm(future,fid,$fid) {
01086     $future Cancel
01087     }
01088 
01089     unset comm(future,fid,$fid)
01090     return
01091 }
01092 
01093 
01094 
01095 
01096 
01097 
01098 
01099 
01100 
01101 
01102 
01103 
01104 
01105 
01106 
01107 
01108 ret  ::comm::commCollect (type chan , type fid) {
01109     variable comm
01110     upvar #0 comm($chan,buf,$fid) data
01111 
01112     # Tcl8 may return an error on read after a close
01113     if {[catch {read $fid} nbuf] || [eof $fid]} {
01114     commDebug {puts stderr "<$chan> collect/lost eof $fid = [eof $fid]"}
01115     commDebug {puts stderr "<$chan> collect/lost nbuf = <$nbuf>"}
01116     commDebug {puts stderr "<$chan> collect/lost [fconfigure $fid]"}
01117 
01118     fileevent $fid readable {}      ;# be safe
01119     commLostConn $chan $fid "target application died or connection lost"
01120     return
01121     }
01122     append data $nbuf
01123 
01124     commDebug {puts stderr "<$chan> collect <$data>"}
01125 
01126     # If data contains at least one complete command, we will
01127     # be able to take off the first element, which is a list holding
01128     # the command.  This is true even if data isn't a well-formed
01129     # list overall, with unmatched open braces.  This works because
01130     # each command in the protocol ends with a newline, thus allowing
01131     # lindex and lreplace to work.
01132     #
01133     # This isn't true with Tcl8.0, which will return an error until
01134     # the whole buffer is a valid list.  This is probably OK, although
01135     # it could potentially cause a deadlock.
01136 
01137     while {![catch {set cmd [lindex $data 0]}]} {
01138     commDebug {puts stderr "<$chan> cmd <$data>"}
01139     if {[string equal "" $cmd]} break
01140     if {[info complete $cmd]} {
01141         set data [lreplace $data 0 0]
01142         after idle \
01143             [list ::comm::commExec $chan $fid $comm($chan,fids,$fid) $cmd]
01144     }
01145     }
01146 }
01147 
01148 
01149 
01150 
01151 
01152 
01153 
01154 
01155 
01156 
01157 
01158 
01159 
01160 
01161 
01162 
01163 ret  ::comm::commExec (type chan , type fid , type remoteid , type buf) {
01164     variable comm
01165 
01166     # buffer should contain:
01167     #   send  # {cmd}       execute cmd and send reply with serial #
01168     #   async # {cmd}       execute cmd but send no reply
01169     #   reply # {cmd}       execute cmd as reply to serial #
01170 
01171     # these variables are documented in the hook interface
01172     set cmd [lindex $buf 0]
01173     set ser [lindex $buf 1]
01174     set buf [lrange $buf 2 end]
01175     set buffer [lindex $buf 0]
01176 
01177     # Save remoteid for "comm remoteid".  This will only be valid
01178     # if retrieved before any additional events occur on this channel.
01179     # N.B. we could have already lost the connection to remote, making
01180     # this id be purely informational!
01181     set comm($chan,remoteid) [set id $remoteid]
01182 
01183     # Save state for possible async result generation
01184     AsyncPrepare $chan $fid $cmd $ser
01185 
01186     commDebug {puts stderr "<$chan> exec <$cmd,$ser,$buf>"}
01187 
01188     switch -- $cmd {
01189     send - async - command {}
01190     callback {
01191         if {![info exists comm($chan,return,$ser)]} {
01192             commDebug {puts stderr "<$chan> No one waiting for serial \"$ser\""}
01193         return
01194         }
01195 
01196         # Decompose reply command to assure it only uses "return"
01197         # with no side effects.
01198 
01199         array set return {-code "" -errorinfo "" -errorcode ""}
01200         set ret [lindex $buffer end]
01201         set len [llength $buffer]
01202         incr len -2
01203         foreach {sw val} [lrange $buffer 1 $len] {
01204         if {![info exists return($sw)]} continue
01205         set return($sw) $val
01206         }
01207 
01208         catch {CommRunHook $chan callback}
01209 
01210         # this wakes up the sender
01211         commDebug {puts stderr "<$chan> --<<wakeup $ser>>--"}
01212 
01213         # the return holds the callback command
01214         # string map the optional %-subs
01215         set args [list -id       $id \
01216               -serial    $ser \
01217               -chan      $chan \
01218               -code      $return(-code) \
01219               -errorcode $return(-errorcode) \
01220               -errorinfo $return(-errorinfo) \
01221               -result    $ret \
01222              ]
01223         set code [catch {uplevel \#0 $comm($chan,return,$ser) $args} err]
01224         catch {unset comm($chan,return,$ser)}
01225 
01226         # remove pending serial
01227         upvar 0 comm($chan,pending,$id) pending
01228         if {[info exists pending]} {
01229         set pos [lsearch -exact $pending [list $ser callback]]
01230         if {$pos != -1} {
01231             set pending [lreplace $pending $pos $pos]
01232         }
01233         }
01234         if {$code} {
01235         commBgerror $err
01236         }
01237         return
01238     }
01239     reply {
01240         if {![info exists comm($chan,return,$ser)]} {
01241             commDebug {puts stderr "<$chan> No one waiting for serial \"$ser\""}
01242         return
01243         }
01244 
01245         # Decompose reply command to assure it only uses "return"
01246         # with no side effects.
01247 
01248         array set return {-code "" -errorinfo "" -errorcode ""}
01249         set ret [lindex $buffer end]
01250         set len [llength $buffer]
01251         incr len -2
01252         foreach {sw val} [lrange $buffer 1 $len] {
01253         if {![info exists return($sw)]} continue
01254         set return($sw) $val
01255         }
01256 
01257         catch {CommRunHook $chan reply}
01258 
01259         # this wakes up the sender
01260         commDebug {puts stderr "<$chan> --<<wakeup $ser>>--"}
01261         set comm($chan,result,$ser) $ret
01262         set comm($chan,return,$ser) [array get return]
01263         return
01264     }
01265     vers {
01266         set ::comm::comm($chan,vers,$id) $ser
01267         return
01268     }
01269     default {
01270         commDebug {puts stderr "<$chan> unknown command; discard \"$cmd\""}
01271         return
01272     }
01273     }
01274 
01275     # process eval hook now
01276     set done 0
01277     set err  0
01278     if {[info exists comm($chan,hook,eval)]} {
01279     set err [catch {CommRunHook $chan eval} ret]
01280     commDebug {puts stderr "<$chan> eval hook res <$err,$ret>"}
01281     switch $err {
01282         1 {
01283         # error
01284         set done 1
01285         }
01286         2 - 3 {
01287         # return / break
01288         set err 0
01289         set done 1
01290         }
01291     }
01292     }
01293 
01294     commDebug {puts stderr "<$chan> hook(eval) done=$done, err=$err"}
01295 
01296     # exec command
01297     if {!$done} {
01298     commDebug {puts stderr "<$chan> exec ($buffer)"}
01299 
01300     # Sadly, the uplevel needs to be in the catch to access the local
01301     # variables buffer and ret.  These cannot simply be global because
01302     # commExec is reentrant (i.e., they could be linked to an allocated
01303     # serial number).
01304 
01305     if {$comm($chan,interp) == {}} {
01306         # Main interpreter
01307         set thecmd [concat [list uplevel \#0] $buffer]
01308         set err    [catch $thecmd ret]
01309     } else {
01310         # Redirect execution into the configured slave
01311         # interpreter. The exact command used depends on the
01312         # capabilities of the interpreter. A best effort is made
01313         # to execute the script in the global namespace.
01314         set interp $comm($chan,interp)
01315 
01316         if {$comm($chan,interp,upl) == "ok"} {
01317         set thecmd [concat [list uplevel \#0] $buffer]
01318         set err [catch {interp eval $interp $thecmd} ret]
01319         } elseif {$comm($chan,interp,aset) == "hidden"} {
01320         set thecmd [linsert $buffer 0 interp invokehidden $interp uplevel \#0]
01321         set err [catch $thecmd ret]
01322         } else {
01323         set thecmd [concat [list interp eval $interp] $buffer]
01324         set err [catch $thecmd ret]
01325         }
01326     }
01327     }
01328 
01329     # Check and handle possible async result generation.
01330     if {[AsyncCheck]} return
01331 
01332     commSendReply $chan $fid $cmd $ser $err $ret
01333     return
01334 }
01335 
01336 
01337 
01338 
01339 
01340 
01341 
01342 
01343 
01344 
01345 
01346 
01347 
01348 
01349 
01350 
01351 ret  ::comm::commSendReply (type chan , type fid , type cmd , type ser , type err , type ret) {
01352     variable comm
01353 
01354     commDebug {puts stderr "<$chan> res <$err,$ret> /$cmd"}
01355 
01356     # The double list assures that the command is a single list when read.
01357     if {[string equal send $cmd] || [string equal command $cmd]} {
01358     # The catch here is just in case we lose the target.  Consider:
01359     #   comm send $other comm send [comm self] exit
01360     catch {
01361         set return [list return -code $err]
01362         # send error or result
01363         if {$err == 1} {
01364         global errorInfo errorCode
01365         lappend return -errorinfo $errorInfo -errorcode $errorCode
01366         }
01367         lappend return $ret
01368         if {[string equal send $cmd]} {
01369         set reply reply
01370         } else {
01371         set reply callback
01372         }
01373         puts  $fid [list [list $reply $ser $return]]
01374         flush $fid
01375     }
01376     commDebug {puts stderr "<$chan> reply sent"}
01377     }
01378 
01379     if {$err == 1} {
01380     commBgerror $ret
01381     }
01382     commDebug {puts stderr "<$chan> exec complete"}
01383     return
01384 }
01385 
01386 ret  ::comm::CommRunHook (type chan , type event) {
01387     variable comm
01388 
01389     # The documentation promises the hook scripts to have access to a
01390     # number of internal variables. For a regular hook we simply
01391     # execute it in the calling level to fulfill this. When the hook
01392     # is redirected into an interpreter however we do a best-effort
01393     # copying of the variable values into the interpreter. Best-effort
01394     # because the 'set' command may not be available in the
01395     # interpreter, not even hidden.
01396 
01397     if {![info exists comm($chan,hook,$event)]} return
01398     set cmd    $comm($chan,hook,$event)
01399     set interp $comm($chan,interp)
01400     commDebug {puts stderr "<$chan> hook($event) run <$cmd>"}
01401 
01402     if {
01403     ($interp != {}) &&
01404     ([lsearch -exact $comm($chan,events) $event] >= 0)
01405     } {
01406     # Best-effort to copy the context into the interpreter for
01407     # access by the hook script.
01408     set vars   {
01409         addr buffer chan cmd fid host
01410         id port reason remport ret var
01411     }
01412 
01413     if {$comm($chan,interp,set) == "ok"} {
01414         foreach v $vars {
01415         upvar 1 $v V
01416         if {![info exists V]} continue
01417         interp eval $interp [list set $v $V]
01418         }
01419     } elseif {$comm($chan,interp,set) == "hidden"} {
01420         foreach v $vars {
01421         upvar 1 $v V
01422         if {![info exists V]} continue
01423         interp invokehidden $interp set $v $V
01424         }
01425     }
01426     upvar 1 return AV
01427     if {[info exists AV]} {
01428         if {$comm($chan,interp,aset) == "ok"} {
01429         interp eval $interp [list array set return [array get AV]]
01430         } elseif {$comm($chan,interp,aset) == "hidden"} {
01431         interp invokehidden $interp array set return [array get AV]
01432         }
01433     }
01434 
01435     commDebug {puts stderr "<$chan> /interp $interp"}
01436     set code [catch {interp eval $interp $cmd} res]
01437     } else {
01438     commDebug {puts stderr "<$chan> /main"}
01439     set code [catch {uplevel 1 $cmd} res]
01440     }
01441 
01442     # Perform the return code propagation promised
01443     # to the hook scripts.
01444     switch -exact -- $code {
01445     0 {}
01446     1 {
01447         return -errorinfo $::errorInfo -errorcode $::errorCode -code error $res
01448     }
01449     3 {return}
01450     4 {}
01451     default {return -code $code $res}
01452     }
01453     return
01454 }
01455 
01456 
01457 
01458 
01459 
01460 
01461 
01462 
01463 
01464 ret  ::comm::AsyncPrepare (type chan , type fid , type cmd , type ser) {
01465     variable comm
01466     set comm(current,async) 0
01467     set comm(current,state) [list $chan $fid $cmd $ser]
01468     return
01469 }
01470 
01471 ret  ::comm::AsyncCheck () {
01472     # Check if the executed command notified us of an async return. If
01473     # not we let the regular return processing handle the end of the
01474     # script. Otherwise we stop the caller from proceeding, preventing
01475     # a regular return.
01476 
01477     variable comm
01478     if {!$comm(current,async)} {return 0}
01479     return 1
01480 }
01481 
01482 
01483 
01484 
01485 
01486 ret  comm::FutureDone (type future , type chan , type fid , type cmd , type sid , type rcode , type rvalue) {
01487     variable comm
01488     commSendReply $chan $fid $cmd $sid $rcode $rvalue
01489 
01490     set pos [lsearch -exact $comm(future,fid,$fid) $future]
01491     set comm(future,fid,$fid) [lreplace $comm(future,fid,$fid) $pos $pos]
01492     return
01493 }
01494 
01495 
01496 
01497 
01498 
01499 
01500 
01501 
01502 
01503 
01504 ret  ::comm::Vwait (type varname) {
01505     variable ::comm::comm
01506 
01507     set hasstate [info exists comm(current,async)]
01508     set hasremote 0
01509     if {$hasstate} {
01510     set chan     [lindex $comm(current,state) 0]
01511     set async    $comm(current,async)
01512     set state    $comm(current,state)
01513     set hasremote [info exists comm($chan,remoteid)]
01514     if {$hasremote} {
01515         set remoteid $comm($chan,remoteid)
01516     }
01517     }
01518 
01519     set code [catch {uplevel 1 [list ::comm::VwaitOrig $varname]} res]
01520 
01521     if {$hasstate} {
01522     set comm(current,async)  $async
01523     set comm(current,state)  $state
01524     }
01525     if {$hasremote} {
01526     set comm($chan,remoteid) $remoteid
01527     }
01528 
01529     return -code $code $res
01530 }
01531 
01532 ret  ::comm::Update (type args) {
01533     variable ::comm::comm
01534 
01535     set hasstate [info exists comm(current,async)]
01536     set hasremote 0
01537     if {$hasstate} {
01538     set chan     [lindex $comm(current,state) 0]
01539     set async    $comm(current,async)
01540     set state    $comm(current,state)
01541 
01542     set hasremote [info exists comm($chan,remoteid)]
01543     if {$hasremote} {
01544         set remoteid $comm($chan,remoteid)
01545     }
01546     }
01547 
01548     set code [catch {uplevel 1 [linsert $args 0 ::comm::UpdateOrig]} res]
01549 
01550     if {$hasstate} {
01551     set comm(current,async)  $async
01552     set comm(current,state)  $state
01553     }
01554     if {$hasremote} {
01555     set comm($chan,remoteid) $remoteid
01556     }
01557 
01558     return -code $code $res
01559 }
01560 
01561 
01562 
01563 ret  ::comm::InitWrappers () {
01564     rename ::vwait       ::comm::VwaitOrig
01565     rename ::comm::Vwait ::vwait
01566 
01567     rename ::update       ::comm::UpdateOrig
01568     rename ::comm::Update ::update
01569 
01570     proc ::comm::InitWrappers {} {}
01571     return
01572 }
01573 
01574 
01575 
01576 
01577 snit::type comm::future {
01578     option -command -default {}
01579 
01580     constructor {chan fid cmd ser} {
01581      xfid =   $fid
01582      xcmd =   $cmd
01583      xser =   $ser
01584      xchan =  $chan
01585     return
01586     }
01587 
01588     destructor {
01589     if {!$canceled} {
01590         return -code error \
01591             "Illegal attempt to destroy unresolved future \"$self\""
01592     }
01593     }
01594 
01595     ret  return (type args) {
01596     # Syntax:             | 0
01597     #       : -code x     | 2
01598     #       : -code x val | 3
01599     #       :         val | 4
01600     # Allowing multiple -code settings, last one is taken.
01601 
01602     set rcode  0
01603     set rvalue {}
01604 
01605     while {[lindex $args 0] == "-code"} {
01606         set rcode [lindex $args 1]
01607         set args  [lrange $args 2 end]
01608     }
01609     if {[llength $args] > 1} {
01610         return -code error "wrong\#args, expected \"?-code errcode? ?result?\""
01611     }
01612     if {[llength $args] == 1} {
01613         set rvalue [lindex $args 0]
01614     }
01615 
01616     if {!$canceled} {
01617         comm::FutureDone $self $xchan $xfid $xcmd $xser $rcode $rvalue
01618         set canceled 1
01619     }
01620     # assert: canceled == 1
01621     $self destroy
01622     return
01623     }
01624 
01625     variable xfid  {}
01626     variable xcmd  {}
01627     variable xser  {}
01628     variable xchan {}
01629     variable canceled 0
01630 
01631     
01632     
01633 
01634     ret  Cancel () {
01635     set canceled 1
01636     if {![llength $options(-command)]} {return}
01637     uplevel #0 [linsert $options(-command) end $self]
01638     return
01639     }
01640 }
01641 
01642 
01643 
01644 ::comm::InitWrappers
01645 
01646 
01647 
01648 
01649 
01650 
01651 if {![info exists ::comm::comm(comm,port)]} {
01652     if {[string equal macintosh $tcl_platform(platform)]} {
01653 	::comm::comm new ::comm::comm -port 0 -local 0 -listen 1
01654 	 ::comm = ::comm(localhost) \
01655         [lindex [fconfigure $::comm::comm(::comm::comm,socket) -sockname] 0]
01656 	::comm::comm config -local 1
01657     } else {
01658 	::comm::comm new ::comm::comm -port 0 -local 1 -listen 1
01659     }
01660 }
01661 
01662 
01663 package provide comm 4.5.6
01664