multiop.tcl
Go to the documentation of this file.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
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
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
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126 package require fileutil ;
00127 package require snit ;
00128 package require struct::stack ;
00129 package require wip ;
00130
00131
00132
00133
00134 snit::type ::fileutil::multi::op {
00135
00136
00137
00138 constructor {args} {} ;
00139
00140
00141
00142
00143 constructor {args} {
00144 install stack using struct::stack::stack ${selfns}::stack
00145 $self wip_up =
00146
00147
00148 defdva \
00149 re Reset = ( Push ) Pop \
00150 into Into in Into from From \
00151 cd ChDir up ChUp as As \
00152 move Move copy Copy remove Remove \
00153 but But not Exclude the The \
00154 except Except for Exclude exclude Exclude \
00155 to Into -> Save the- TheSet = \
00156 recursive Recursive recursively Recursive \
00157 for-win ForWindows for-unix ForUnix \
00158 for-windows ForWindows expand Expand \
00159 invoke Invoke strict Strict !strict NotStrict \
00160 files Files links Links all Everything \
00161 dirs Directories directories Directories \
00162 state? QueryState from? QueryFrom into? QueryInto \
00163 excluded? QueryExcluded as? QueryAs type? QueryType \
00164 recursive? QueryRecursive operation? QueryOperation \
00165 strict? QueryStrict !recursive NotRecursive
00166
00167 $self Re
00168 runl = $args
00169 return
00170 }
00171
00172 destructor {
00173 $wip destroy
00174 return
00175 }
00176
00177 ret do (type args) {
00178 return [runl $args]
00179 }
00180
00181
00182
00183 wip::dsl
00184
00185
00186 ret Reset () {
00187 $stack clear
00188 set base ""
00189 set alias ""
00190 set op ""
00191 set recursive 0
00192 set src ""
00193 set excl ""
00194 set types {}
00195 set strict 0
00196 return
00197 }
00198
00199
00200 ret Push () {
00201 $stack push [list $base $alias $op $opcmd $recursive $src $excl $types $strict]
00202 return
00203 }
00204
00205 ret Pop () {
00206 if {![$stack size]} {
00207 return -code error {Stack underflow}
00208 }
00209 foreach {base alias op opcmd recursive src excl types strict} [$stack pop] break
00210 return
00211 }
00212
00213
00214 ret Into (type dir) {
00215 if {$dir eq ""} {set dir [pwd]}
00216 if {$strict && ![fileutil::test $dir edr msg {Destination directory}]} {
00217 return -code error $msg
00218 }
00219 set base $dir
00220 return
00221 }
00222
00223 ret ChDir (type dir) { $self Into [file join $base $dir] ; return }
00224 ret ChUp () { $self Into [file dirname $base] ; return }
00225
00226
00227 ret As (type fname) {
00228 set alias [ForceRelative $fname]
00229 return
00230 }
00231
00232
00233 ret Move () { set op move ; return }
00234 ret Copy () { set op copy ; return }
00235 ret Remove () { set op remove ; return }
00236 ret Expand () { set op expand ; return }
00237
00238 ret Invoke (type cmdprefix) {
00239 set op invoke
00240 set opcmd $cmdprefix
00241 return
00242 }
00243
00244
00245 ret Recursive () { set recursive 1 ; return }
00246 ret NotRecursive () { set recursive 0 ; return }
00247
00248
00249 ret From (type dir) {
00250 if {$dir eq ""} {set dir [pwd]}
00251 if {![fileutil::test $dir edr msg {Source directory}]} {
00252 return -code error $msg
00253 }
00254 set src $dir
00255 return
00256 }
00257
00258
00259 ret But () { run_next_while {not exclude} ; return }
00260 ret Except () { run_next_while {for} ; return }
00261
00262 ret Exclude (type pattern) {
00263 lappend excl $pattern
00264 return
00265 }
00266
00267
00268 ret The (type pattern) {
00269 run_next_while {as but except exclude from into in to files dirs directories links all}
00270
00271 switch -exact -- $op {
00272 invoke {Invoke [Resolve [Remember [Exclude [Expand $src $pattern]]]]}
00273 move {Move [Resolve [Remember [Exclude [Expand $src $pattern]]]]}
00274 copy {Copy [Resolve [Remember [Exclude [Expand $src $pattern]]]]}
00275 remove {Remove [Remember [Exclude [Expand $base $pattern]]] }
00276 expand { Remember [Exclude [Expand $base $pattern]] }
00277 }
00278
00279 # Reset the per-pattern flags of the resolution context back
00280 # to their defaults, for the next pattern.
00281
00282 set alias {}
00283 set excl {}
00284 set recursive 0
00285 return
00286 }
00287
00288
00289
00290
00291
00292
00293 ret TheSet (type varname) {
00294 # See 'Save' for the levels we jump here.
00295 upvar 5 $varname var
00296
00297 run_next_while {as from into in to}
00298
00299 switch -exact -- $op {
00300 invoke {Invoke [Resolve $var]}
00301 move {Move [Resolve $var]}
00302 copy {Copy [Resolve $var]}
00303 remove {Remove $var }
00304 expand {
00305 return -code error "Expansion does not make sense\
00306 when we already have a set of files."
00307 }
00308 }
00309
00310 # Reset the per-pattern flags of the resolution context back
00311 # to their defaults, for the next pattern.
00312
00313 set alias {}
00314 return
00315 }
00316
00317
00318
00319 ret Save (type varname) {
00320 # Levels to jump. Brittle.
00321 # 5: Caller
00322 # 4: object do ...
00323 # 3: runl
00324 # 2: wip::runl
00325 # 1: run_next
00326 # 0: Here
00327 upvar 5 $varname v
00328 set v $lastexpansion
00329 return
00330 }
00331
00332
00333
00334 ret ForUnix () {
00335 global tcl_platform
00336 if {$tcl_platform(platform) eq "unix"} return
00337 # Kill the remaining code. This effectively aborts processing.
00338 replacel {}
00339 return
00340 }
00341
00342 ret ForWindows () {
00343 global tcl_platform
00344 if {$tcl_platform(platform) eq "windows"} return
00345 # Kill the remaining code. This effectively aborts processing.
00346 replacel {}
00347 return
00348 }
00349
00350
00351
00352 ret Strict () {
00353 set strict 1
00354 return
00355 }
00356
00357 ret NotStrict () {
00358 set strict 0
00359 return
00360 }
00361
00362
00363
00364 ret Files () {
00365 set types files
00366 return
00367 }
00368
00369 ret Links () {
00370 set types links
00371 return
00372 }
00373
00374 ret Directories () {
00375 set types dirs
00376 return
00377 }
00378
00379 ret Everything () {
00380 set types {}
00381 return
00382 }
00383
00384
00385
00386 ret QueryState () {
00387 return [list \
00388 from $src \
00389 into $base \
00390 as $alias \
00391 op $op \
00392 excluded $excl \
00393 recursive $recursive \
00394 type $types \
00395 strict $strict \
00396 ]
00397 }
00398 ret QueryExcluded () {
00399 return $excl
00400 }
00401 ret QueryFrom () {
00402 return $src
00403 }
00404 ret QueryInto () {
00405 return $base
00406 }
00407 ret QueryAs () {
00408 return $alias
00409 }
00410 ret QueryOperation () {
00411 return $op
00412 }
00413 ret QueryRecursive () {
00414 return $recursive
00415 }
00416 ret QueryType () {
00417 return $types
00418 }
00419 ret QueryStrict () {
00420 return $strict
00421 }
00422
00423
00424
00425
00426 component stack ;
00427 variable base "" ;
00428 variable alias "" ;
00429 variable op "" ;
00430 variable opcmd "" ;
00431 variable recursive 0 ;
00432 variable src "" ;
00433 variable excl "" ;
00434
00435 variable types {} ;
00436 variable strict 0 ;
00437
00438 variable lastexpansion "" ;
00439
00440
00441
00442
00443 ret ForceRelative (type path) {
00444 set pathtype [file pathtype $path]
00445 switch -exact -- $pathtype {
00446 relative {
00447 return $path
00448 }
00449 absolute {
00450 # Chop off the first element in the path, which is the
00451 # root, either '/' or 'x:/'. If this was the only
00452 # element assume an empty path.
00453
00454 set path [lrange [file split $path] 1 end]
00455 if {![llength $path]} {return {}}
00456 return [eval [linsert $path 0 file join]]
00457 }
00458 volumerelative {
00459 return -code error {Unable to handle volumerelative path, yet}
00460 }
00461 }
00462
00463 return -code error \
00464 "file pathtype returned unknown type \"$pathtype\""
00465 }
00466
00467 ret ForceAbsolute (type path) {
00468 return [file join [pwd] $path]
00469 }
00470
00471
00472
00473
00474 ret Invoke (type files) {
00475 upvar 1 base base src src opcmd opcmd
00476 uplevel #0 [linsert $opcmd end $src $base $files]
00477 return
00478 }
00479
00480 ret Move (type files) {
00481 upvar 1 base base src src
00482
00483 foreach {s d} $files {
00484 set s [file join $src $s]
00485 set d [file join $base $d]
00486
00487 file mkdir [file dirname $d]
00488 file rename -force $s $d
00489 }
00490 return
00491 }
00492
00493 ret Copy (type files) {
00494 upvar 1 base base src src
00495
00496 foreach {s d} $files {
00497 set s [file join $src $s]
00498 set d [file join $base $d]
00499
00500 file mkdir [file dirname $d]
00501 file copy -force $s $d
00502 }
00503 return
00504 }
00505
00506 ret Remove (type files) {
00507 upvar 1 base base
00508
00509 foreach f $files {
00510 file delete -force [file join $base $f]
00511 }
00512 return
00513 }
00514
00515
00516
00517
00518 typevariable tmap -array {
00519 files {f TFile}
00520 links {l TLink}
00521 dirs {d TDir}
00522 {} {{} {}}
00523 }
00524
00525 ret Expand (type dir , type pattern) {
00526 upvar 1 recursive recursive strict strict types types tmap tmap
00527 # FUTURE: struct::list filter ...
00528
00529 set files {}
00530 if {$recursive} {
00531 # Recursion through the entire directory hierarchy, save
00532 # all matching paths.
00533
00534 set filter [lindex $tmap($types) 1]
00535 if {$filter ne ""} {
00536 set filter [myproc $filter]
00537 }
00538
00539 foreach f [fileutil::find $dir $filter] {
00540 if {![string match $pattern [file tail $f]]} continue
00541 lappend files [fileutil::stripPath $dir $f]
00542 }
00543 } else {
00544 # No recursion, just scan the whole directory for matching paths.
00545 # check for specific types integrated.
00546
00547 set filter [lindex $tmap($types) 0]
00548 if {$filter ne ""} {
00549 foreach f [glob -nocomplain -directory $dir -types $filter -- $pattern] {
00550 lappend files [fileutil::stripPath $dir $f]
00551 }
00552 } else {
00553 foreach f [glob -nocomplain -directory $dir -- $pattern] {
00554 lappend files [fileutil::stripPath $dir $f]
00555 }
00556 }
00557 }
00558
00559 if {[llength $files]} {return $files}
00560 if {!$strict} {return {}}
00561
00562 return -code error \
00563 "No files matching pattern \"$pattern\" in directory \"$dir\""
00564 }
00565
00566 ret TFile (type f) {file isfile $f}
00567 ret TDir (type f) {file isdirectory $f}
00568 ret TLink (type f) {expr {[file type $f] eq "link"}}
00569
00570 ret Exclude (type files) {
00571 upvar 1 excl excl
00572
00573 # FUTURE: struct::list filter ...
00574 set res {}
00575 foreach f $files {
00576 if {[IsExcluded $f $excl]} continue
00577 lappend res $f
00578 }
00579 return $res
00580 }
00581
00582 ret IsExcluded (type f , type patterns) {
00583 foreach p $patterns {
00584 if {[string match $p $f]} {return 1}
00585 }
00586 return 0
00587 }
00588
00589 ret Resolve (type files) {
00590 upvar 1 alias alias
00591 set res {}
00592 foreach f $files {
00593
00594 # Remember alias for processing and auto-invalidate to
00595 # prevent contamination of the next file.
00596
00597 set thealias $alias
00598 set alias ""
00599
00600 if {$thealias eq ""} {
00601 set d $f
00602 } else {
00603 set d [file dirname $f]
00604 if {$d eq "."} {
00605 set d $thealias
00606 } else {
00607 set d [file join $d $thealias]
00608 }
00609 }
00610
00611 lappend res $f $d
00612 }
00613 return $res
00614 }
00615
00616 ret Remember (type files) {
00617 upvar 1 lastexpansion lastexpansion
00618 set lastexpansion $files
00619 return $files
00620 }
00621
00622
00623
00624
00625 }
00626
00627
00628
00629
00630 package provide fileutil::multi::op 0.5
00631