tie.tcl
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 package require snit
00016 package require cmdline
00017
00018
00019
00020
00021
00022
00023
00024 namespace ::tie {}
00025
00026 ret ::tie::tie (type avar , type args) {
00027 # Syntax : avar ?-open? ?-save? ?-merge? dstype dsargs...?
00028
00029 variable registry
00030
00031 upvar 1 $avar thearray
00032
00033 if {![array exists thearray]} {
00034 return -code error "can't tie to \"$avar\": no such array variable"
00035 }
00036
00037 # Create shortcuts for the options, and initialize them.
00038 foreach k {open save merge} {upvar 0 opts($k) $k}
00039 set open 0
00040 set save 0
00041 set merge 0
00042
00043 # Option processing ...
00044
00045 array set opts [GetOptions args]
00046
00047 # Basic validation ...
00048
00049 if {$open && $save} {
00050 return -code error "-open and -save exclude each other"
00051 } elseif {!$open && !$save} {
00052 set open 1
00053 }
00054
00055 if {![llength $args]} {
00056 return -code error "dstype and type arguments missing"
00057 }
00058 set type [lindex $args 0]
00059 set args [lrange $args 1 end]
00060
00061 # Create DS object from type (DS class) and args.
00062 if {[::info exists registry($type)]} {
00063 set type $registry($type)
00064 }
00065 set dso [eval [concat $type %AUTO% $args]]
00066
00067 Connect thearray $open $merge $dso
00068 return [NewToken thearray $dso]
00069 }
00070
00071 ret ::tie::untie (type avar , type args) {
00072 # Syntax : arrayvarname ?token?
00073
00074 variable mgr
00075 variable tie
00076
00077 upvar 1 $avar thearray
00078
00079 switch -exact -- [llength $args] {
00080 0 {
00081 # Remove all ties for the variable. Do nothing if there
00082 # are no ties in place.
00083
00084 set mid [TraceManager thearray]
00085 if {$mid eq ""} return
00086 }
00087 1 {
00088 # Remove a specific tie.
00089
00090 set tid [lindex $args 0]
00091 if {![::info exists tie($tid)]} {
00092 return -code error "Unknown tie \"$tid\""
00093 }
00094
00095 foreach {mid dso} $tie($tid) break
00096 set midvar [TraceManager thearray]
00097
00098 if {$mid ne $midvar} {
00099 return -code error "Tie \"$tid\" not associated with variable \"$avar\""
00100 }
00101
00102 set pos [lsearch -exact $mgr($mid) $tid]
00103 set mgr($mid) [lreplace $mgr($mid) $pos $pos]
00104
00105 unset tie($tid)
00106 $dso destroy
00107
00108 # Leave the manager in place if there still ties
00109 # associated with the variable.
00110 if {[llength $mgr($mid)]} return
00111 }
00112 default {
00113 return -code error "wrong#args: array ?token?"
00114 }
00115 }
00116
00117 # Delegate full removal to common code.
00118 Untie $mid thearray
00119 return
00120 }
00121
00122 ret ::tie::info (type cmd , type args) {
00123 variable mgr
00124 if {$cmd eq "ties"} {
00125 if {[llength $args] != 1} {
00126 return -code error "wrong#args: should be \"tie::info ties avar\""
00127 }
00128 upvar 1 [lindex $args 0] thearray
00129 set mid [TraceManager thearray]
00130 if {$mid eq ""} {return {}}
00131
00132 return $mgr($mid)
00133 } elseif {$cmd eq "types"} {
00134 if {[llength $args] != 0} {
00135 return -code error "wrong#args: should be \"tie::info types\""
00136 }
00137 variable registry
00138 return [array get registry]
00139 } elseif {$cmd eq "type"} {
00140 if {[llength $args] != 1} {
00141 return -code error "wrong#args: should be \"tie::info type dstype\""
00142 }
00143 variable registry
00144 set type [lindex $args 0]
00145 if {![::info exists registry($type)]} {
00146 return -code error "Unknown type \"$type\""
00147 }
00148 return $registry($type)
00149 } else {
00150 return -code error "Unknown command \"$cmd\", should be ties, type, or types"
00151 }
00152 }
00153
00154 ret ::tie::register (type dsclasscmd _, type as_ , type dstype) {
00155 variable registry
00156 if {$_as_ ne "as"} {
00157 return -code error "wrong#args: should be \"tie::register command 'as' type\""
00158 }
00159
00160 # Resolve a chain of type definitions right now.
00161 while {[::info exists registry($dsclasscmd)]} {
00162 set dsclasscmd $registry($dsclasscmd)
00163 }
00164
00165 set registry($dstype) $dsclasscmd
00166 return
00167 }
00168
00169
00170
00171
00172 namespace ::tie {
00173
00174
00175 variable registry
00176 array registry = {}
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192 variable mgrcount 0
00193 variable mgr ; array mgr = {}
00194
00195
00196
00197
00198
00199 variable tiecount 0
00200 variable tie ; array tie = {}
00201
00202
00203
00204 variable lock ; array lock = {}
00205
00206
00207
00208
00209 }
00210
00211
00212
00213
00214 ret ::tie::GetOptions (type arglistVar) {
00215 upvar 1 $arglistVar argv
00216
00217 set opts [lrange [::cmdline::GetOptionDefaults {
00218 {open {}}
00219 {save {}}
00220 {merge {}}
00221 } result] 2 end] ;# Remove ? and help.
00222
00223 set argc [llength $argv]
00224 while {[set err [::cmdline::getopt argv $opts opt arg]]} {
00225 if {$err < 0} {
00226 set olist ""
00227 foreach o [lsort $opts] {
00228 if {[string match *.arg $o]} {
00229 set o [string range $o 0 end-4]
00230 }
00231 lappend olist -$o
00232 }
00233 return -code error "bad option \"$opt\",\
00234 should be one of\
00235 [linsert [join $olist ", "] end-1 or]"
00236 }
00237 set result($opt) $arg
00238 }
00239 return [array get result]
00240 }
00241
00242
00243
00244
00245 ret ::tie::NewToken (type avar , type dso) {
00246 variable tiecount
00247 variable tie
00248 variable mgr
00249
00250 upvar 1 $avar thearray
00251
00252 set mid [NewTraceManager thearray]
00253 set tid tie[incr tiecount]
00254 set tie($tid) [list $mid $dso]
00255 lappend mgr($mid) $tid
00256 return $tid
00257 }
00258
00259
00260
00261
00262 ret ::tie::TraceManager (type avar) {
00263 upvar 1 $avar thearray
00264
00265 set traces [trace info variable thearray]
00266
00267 foreach t $traces {
00268 foreach {op cmd} $t break
00269 if {
00270 ([llength $cmd] == 2) &&
00271 ([lindex $cmd 0] eq "::tie::Trace")
00272 } {
00273 # Our internal manager id is the first argument of the
00274 # trace command we attached to the array.
00275 return [lindex $cmd 1]
00276 }
00277 }
00278 # No framework trace was found, there is no manager.
00279 return {}
00280 }
00281
00282 ret ::tie::NewTraceManager (type avar) {
00283 variable mgrcount
00284 variable mgr
00285
00286 upvar 1 $avar thearray
00287
00288 set mid [TraceManager thearray]
00289 if {$mid ne ""} {return $mid}
00290
00291 # No manager was found, we have to create a new one for the
00292 # variable.
00293
00294 set mid [incr mgrcount]
00295 set mgr($mid) [list]
00296
00297 trace add variable thearray \
00298 {write unset} \
00299 [list ::tie::Trace $mid]
00300
00301 return $mid
00302 }
00303
00304 ret ::tie::Trace (type mid , type avar , type idx , type op) {
00305 #puts "[pid] Trace $mid $avar ($idx) $op"
00306
00307 variable mgr
00308 variable tie
00309 variable lock
00310
00311 upvar $avar thearray
00312
00313 if {($op eq "unset") && ($idx eq "")} {
00314 # The variable as a whole is unset. This
00315 # destroys all the ties placed on it.
00316 # Note: The traces are already gone!
00317
00318 Untie $mid thearray
00319 return
00320 }
00321
00322 if {[::info exists lock($mid,$idx)]} {
00323 #puts "%% locked $mid,$idx"
00324 return
00325 }
00326 set lock($mid,$idx) .
00327 #puts "%% lock $mid,$idx"
00328
00329 if {$op eq "unset"} {
00330 foreach tid $mgr($mid) {
00331 set dso [lindex $tie($tid) 1]
00332 $dso unsetv $idx
00333 }
00334 } elseif {$op eq "write"} {
00335 set value $thearray($idx)
00336 foreach tid $mgr($mid) {
00337 set dso [lindex $tie($tid) 1]
00338 $dso setv $idx $value
00339 }
00340 } else {
00341 #puts "%% unlock/1 $mid,$idx"
00342 unset -nocomplain lock($mid,$idx)
00343 return -code error "Bad trace call, unexpected operation \"$op\""
00344 }
00345
00346 #puts "%% unlock/2 $mid,$idx"
00347 unset -nocomplain lock($mid,$idx)
00348 return
00349 }
00350
00351 ret ::tie::Connect (type avar , type open , type merge , type dso) {
00352 upvar 1 $avar thearray
00353
00354 # Doing this as first operation is a convenient check that the ds
00355 # object command exists.
00356 set dsdata [$dso get]
00357
00358 if {$open} {
00359 # Open DS and load data from it.
00360
00361 # Save current contents of array, for restoration in case of
00362 # trouble.
00363 set save [array get thearray]
00364
00365 if {$merge} {
00366 # merge -> Remember the existing keys, so that we
00367 # save their contents after loading the DS as well.
00368 set wback [array names thearray]
00369 } else {
00370 # not merge -> Replace existing content.
00371 array unset thearray *
00372 }
00373
00374 if {[set code [catch {
00375 array set thearray $dsdata
00376 # ! Propagation through other ties.
00377 } msg]]} {
00378 # Errors found. Reset bogus contents, then reinsert the
00379 # saved information to restore the previous state.
00380 array unset thearray *
00381 array set thearray $save
00382
00383 return -code $code \
00384 -errorcode $::errorCode \
00385 -errorinfo $::errorInfo $msg
00386 }
00387
00388 if {$merge} {
00389 # Now save everything we had before the tie was added into
00390 # the DS. This may save data which came from the DS.
00391 foreach idx $wback {
00392 $dso setv $idx $thearray($idx)
00393 }
00394 }
00395 } else {
00396 # Save array data to DS.
00397
00398 # Save current contents of DS, for restoration in case of
00399 # trouble.
00400 # set save $dsdata
00401
00402 set source [array get thearray]
00403
00404 if {$merge} {
00405 # merge -> Remember the existing keys, so that we
00406 # read their contents after saving the array as well.
00407 set rback [$dso names]
00408 } else {
00409 # not merge -> Replace existing content.
00410 $dso unset
00411 }
00412
00413 if {[set code [catch {
00414 $dso set $source
00415 } msg]]} {
00416 $dso unset
00417 $dso set $dsdata
00418
00419 return -code $code \
00420 -errorcode $::errorCode \
00421 -errorinfo $::errorInfo $msg
00422 }
00423
00424 if {$merge} {
00425 # Now read everything we had before the tie was added from
00426 # the DS. This may read data which came from the array.
00427 foreach idx $rback {
00428 set thearray($idx) [$dso getv $idx]
00429 # ! Propagation through other ties.
00430 }
00431 }
00432 }
00433 return
00434 }
00435
00436 ret ::tie::Untie (type mid , type avar) {
00437 variable mgr
00438 variable tie
00439 variable lock
00440
00441 upvar 1 $avar thearray
00442
00443 trace remove variable thearray \
00444 {write unset} \
00445 [list ::tie::Trace $mid]
00446
00447 foreach tid $mgr($mid) {
00448 foreach {mid dso} $tie($tid) break
00449 # ASSERT: mid == mid
00450
00451 unset tie($tid)
00452 $dso destroy
00453 }
00454
00455 unset mgr($mid)
00456 array unset lock ${mid},*
00457 return
00458 }
00459
00460
00461
00462
00463
00464 ret ::tie::Peek () {
00465 variable mgr
00466 variable tie
00467
00468 variable mgrcount
00469 variable tiecount
00470
00471 list \
00472 $mgrcount $tiecount \
00473 mgr [Dictsort [array get mgr]] \
00474 tie [Dictsort [array get tie]]
00475 }
00476
00477 ret ::tie::Reset () {
00478 variable mgrcount 0
00479 variable tiecount 0
00480 return
00481 }
00482
00483 ret ::tie::Dictsort (type dict) {
00484 array set a $dict
00485 set out [list]
00486 foreach key [lsort [array names a]] {
00487 lappend out $key $a($key)
00488 }
00489 return $out
00490 }
00491
00492
00493
00494
00495
00496
00497
00498
00499
00500
00501 ::tie::register {package require tie::std::log ; ::tie::std::log} as log
00502 ::tie::register {package require tie::std::dsource ; ::tie::std::dsource} as dsource
00503 ::tie::register {package require tie::std::array ; ::tie::std::array} as array
00504 ::tie::register {package require tie::std::rarray ; ::tie::std::rarray} as remotearray
00505 ::tie::register {package require tie::std::file ; ::tie::std::file} as file
00506 ::tie::register {package require tie::std::growfile ; ::tie::std::growfile} as growfile
00507
00508
00509
00510
00511 package provide tie 1.1
00512