1# <20190707.1312.32> 2 3 4# These should use the "quick" option, I think... 5proc CmdToright {} { 6 global glob 7 NewPwd right $glob(left,pwd) 8 UpdateWindow_ right 1 9} 10 11proc CmdToleft {} { 12 global glob 13 NewPwd left $glob(right,pwd) 14 UpdateWindow_ left 1 15} 16 17proc CmdSwapWindows {} { 18 global glob 19 set tmpright $glob(left,pwd) 20 set glob(forceupdate) 1 21 CmdToleft 22 NewPwd right $tmpright 23 UpdateWindow right 24 set glob(forceupdate) 0 25} 26 27proc BatchReceiveVFS { inst } { 28 global glob 29 if {[IsVFS $glob($inst,pwd)]} { 30 PopInfo [_ "You can only issue a receive request to a non-VFS directory"] 31 return 32 } 33 set olddir $glob([Opposite $inst],pwd) 34 foreach itemblock $glob(batchlist) { 35 set item [lindex $itemblock 0] 36 if {![IsVFS $item]} { 37 PopWarn [_ "Can't parse %s as VFS file" $item] 38 } else { 39 NewPwd [Opposite $inst] $VFStok/ 40 set r [Try { VFSgetFile $item \ 41 "$glob($inst,pwd)/[file tail $item]" \ 42 [lindex $itemblock 1] 0 } "" 1] 43 } 44 } 45 set glob(batchlist) {} 46 set glob(forceupdate) 1 47 NewPwd [Opposite $inst] $olddir 48 UpdateWindow both 49 set glob(forceupdate) 0 50} 51 52proc SandDsame {} { 53 smart_dialog .apop[incr ::uni] . [_ "Oops That doesn't work"] \ 54 [list [_ "Please set different source and destination directories!"]]\ 55 0 1 [_ "OK"] 56} 57 58proc typeMatch {ttype tfile ftype ffile} { 59 set fType [expr {[string index $ftype end] == "d" ? "d" : "f"}] 60 if {$fType == $ttype} { 61 return 1 62 } 63 if {$ttype == "f"} { 64 set rt "directory:" 65 set tt " file:" 66 } else { 67 set rt " file:" 68 set tt "directory:" 69 } 70 # Lets not allow replacing files with dirs or dirs with files 71 PopWarn [_ "If target exist it must be the same (file or directory) as current.\ 72 \n%s %s\ 73 \n%s %s" $rt $ffile $tt $tfile] 74 return 0 75} 76 77 78# So here are a few observations: 79# 1) fr always does recursive copies, set up the 'cp' command to do this 80# 2) some copy code will not copy a dirs contents unless we have a trailing / 81# 3) other copy code does not care. 82# 4) if the destination ends with a dir the data (files and dirs) go there. 83# 5) if the destination ends with an undefined dir, (i.e. on the destination) one is created 84# 6) the "file tail" command will eliminate a trailing / and return 85# the resulting tail 86# 7) if the destination dir exists, it is used as is, i.e. existing files that 87# are not in the source are preserved. 88# 8) For files, the new file replaces the old. If it is desired that this only 89# happen if the file differs, see rsync copy. 90# 9) The MSW xcopy wanting to assume "/*" is defeated by appending tail to the 91# destination. 92# 10) By definition when a user says copy/move he is refering to the opposite 93# pane. Both 'file copy' and cp, given a source dir and a destination 94# dir do different things depending on that dirs existance cp: (create if not 95# and make a mirror image, or, if it exists, copy to within). 'file copy' 96# on the other hand refuses to do the copy. This is NOT what we want and thus 97# we have written 'frFileCopy' using 'file copy' and 'glob' to get the 98# desired outcome. 99# 11) In addition neither can be forced to delete the collision dir. 100# 101# So, 102# A) always end dir copy sources with / 103# B) to preserver the trash/recycle bin delete files using delete code, 104# not cp code. 105# C) always append source "tail" to the destination. This may be altered by 106# move/copy as code (which is required for local copies depending on 107# existance of that dir on local dir to dir copies. 108# D) VFS put/get may also need work in this regard. 109 110# CopyCore 111# fromList a list of files to copy each entry-> {file type size} 112# topwd the directory to copy them to 113# inst the pane (left or right) 114# as 1 if we are to ask for new name(s) 115# resume 1 if possible restart, else 0 116# option "cp" for copy "hardlink" for hard linking instead 117 118proc CopyCore {fromList topwd inst {as 0} resume option} { 119 global config glob 120 # This is intended to be the core copy code. If "as" is true 121 # it asks for a destination. It does all the permission tests 122 # if overlaying files and permission has been requested. 123 # The fromlist should be a list of lists (a sublist for each file) 124 # The first entry should be the file name, the second its type & 125 # The third entry its size (used with VFS) 126 # Other code should assemble the fromlist. 127 # todir is a dir 128 # inst should be the 'from' panel as we may need to modify the 'to' panel 129 130 # first take care of the "as" 131 set copyList {} 132 foreach ent $fromList { 133 lassign $ent file type size 134 if {$type == "fl"} { 135 # size unknown on VFS links 136 set size -1 137 } 138 set ::ssdTmp [URL norm $topwd/[file tail $file]] 139 set typeD [expr {[string index $type end] == "d"}] 140 if {$as} { 141 set query [expr {$typeD ? [_ "directory"] : [_ "file"]}] 142 143 set r [smart_dialog .copy[incr ::uni] "." [_ "Copy As..."] \ 144 [list [_ "Please enter new name for destination %s" $query]] \ 145 1 4 \ 146 [list \ 147 [list {} [list -textvariable ::ssdTmp -width 70]]\ 148 [_ OK] [_ "Skip This File"] [_ "Cancel all copies"]]\ 149 [buildDialogConfig] 150 ] 151 # 0,1 use result 2 skip this file -1,3 cancel the whole thing. 152 if {$r == -1 || $r == 3} {return} 153 if {$r == 2} {continue} 154 # Make sure it is not a copy into itself.. 155 } 156 if {$file == $::ssdTmp} { 157 SandDsame 158 return 0 159 } 160 # Or a recursive self copy... 161 if {$typeD && [string first $file/ $::ssdTmp] == 0} { 162 PopError [_ "Can't %s recursively into same directory (%s -> %s)"\ 163 $option $file $::ssdTmp] 164 return 0 165 } 166 set tType [fileType $::ssdTmp] 167 # tType may have any of three values d=>directory f=>file 0=>does not exist 168 if {$tType != 0 && ![typeMatch $tType $::ssdTmp $type $file]} { 169 return 170 } 171 # pass the tType here as it indicates existance 172 lappend copyList [list $file $typeD $size $::ssdTmp $tType] 173 } 174 # frputs copyList fromList 175 # now we need to check permissions... 176 # do we need to?? 177 # We don't delete on copy of dirs... 178 # set dirA $config(ask,dir_delete) 179 set fileA $config(ask,file_delete) 180 # Build a list of asks 181 set testThese {} 182 set doThese {} 183 set dirCount 0 184 set fileCount 0 185 foreach ent $copyList { 186 lassign $ent file typeD size dest dType 187 #set this [string index $type end] 188 frputs file typeD size dest dType 189 if {!$typeD && $fileA && $dType != 0} { 190 frputs ent 191 lappend testThese $ent 192 incr fileCount 193 } else { 194 lappend doThese $ent 195 } 196 } 197 # At this point we have two lists: 198 # doThese, the files to copy unconditionally and 199 # testThese, the files for which we need permission to copy 200 201 # This keeps track of global yes/no from the ok to delete query 202 # 3 global yes 203 # 4 global no 204 # Other vaues -> unknown 205 206 # deleteQuery returns as follows: 207 # -1 s/he Xed out the window (taken to mean cancel all copies) 208 # 0 yes to this file/dir 209 # 1 no to this file/dir 210 # 2 same a -1, cancel all copies.. 211 # 3 yes to all files or dirs (depending on if a file or dir question) 212 # 4 no to all files or dirs Cancels copy of of files or dirs in this list 213 214 set globalYNfile 0 215 set globalYNdir 0 216 set ty "file" 217 set query [_ "file"] 218 219 foreach ent $testThese { 220 # we only test files, not dirs... 221 lassign $ent file typeD size dest dType 222 # frputs file type size dest globalYNfile globalYNdir 223 switch -exact [set globalYN$ty] { 224 4 {continue} 225 0 - 226 1 {set globalYN$ty [deleteQuery $query $dest $fileCount [_ "Copy"]]} 227 } 228 switch -exact [set globalYN$ty] { 229 1 - 230 4 {continue} 231 2 {return 0} 232 } 233 # Here we have the ok, lets do it. 234 lappend doThese $ent 235 } 236 # Here is where we can go async. All we do is to pass the list 237 # and option to an action function, either just a call (not async) 238 # or via "after" for async. 239 240 # if {$glob(async)} { 241 # after 1 [list copyAction $doThese $resume $option] 242 # return 1 243 # } else { 244 # return [copyAction $doThese $resume $option] 245 # } 246 Try {copyAction $doThese $resume $option} "" 1 $glob(async) 247} 248proc copyAction {doThese resume option} { 249 global glob config 250 251 252 # All tests are done. All that is left is to do the copy 253 # We divide this up in ways that make it easy... 254 # further from is either a file or a dir 255 # There are these copies: 256 # 0 & 1 from & to local file system (even is file, odd dir) 257 # 2 & 3 from local to VFS 258 # 4 & 5 from VFS to local 259 # 6 & 7 from VFS to VFS (either within the same VFS or not) 260 # all that sounds like a switch is in order 261 262 # set olddir $glob($inst,pwd) 263 foreach ent $doThese { 264 lassign $ent ffile typeD size dest tType 265 if {[CheckAbort [_ "Copy"]]} { 266 UpdateIf $dest $dest/x 267 return 0 268 } 269 set sw [expr {($typeD) +\ 270 2 * [IsVFS $dest] + \ 271 4 * [IsVFS $ffile] }] 272 if {$option == "hardlink"} { 273 if {$sw > 1 && $sw < 6 ||\ 274 $sw > 5 && [string first $VFStok $dest] != 0} { 275 PopWarn [_ "Hard link not possible across file\ 276 systems\n%s to\n%s" $ffile $dest] 277 return 0 278 } elseif {$sw < 2} { 279 # here we run down links (i.e. we always dereference on copy) 280 set ffile [file dir [file norm $ffile/x]] 281 } 282 } 283 if {$typeD} { 284 set ffile $ffile/ 285 } 286 if {$tType == "f"} { 287 # We are either async or not, don't let it go another level... 288 deleteFile $dest $tType 289 } 290 frputs sw ffile dest 291 switch -exact $sw { 292 0 { 293 # This is the file copy or hardlink 294 # configure the command... 295 set cpcmd $config(cmd,$option) 296 #set to $topwd 297 if {$cpcmd == {}} { 298 if {$option == "cp"} { 299 set cpcmd "frFileCopy" 300 } else { 301 set cpcmd "file link -hard %2@s" 302 set to $tof 303 } 304 } else { 305 set cpcmd [list exec {*}[subst {*}$::stOps $cpcmd]] 306 # if {$glob(async)} { 307 # append cpcmd " %b &" 308 # } 309 } 310 # frputs "pre frECF: " ffile topwd 311 frECF [list {*}$cpcmd]\ 312 [list $ffile $dest] 313 # [list -b $glob(async)] 314 315 } 316 1 { 317 # This is local to local dir copy 318 Log [_ "%s (d) %s >> %s" $option $ffile $dest] 319 320 set cpcmd $config(cmd,$option) 321 if {$option == "cp"} { 322 lassign $config(cmd,dircp) cpcmd dirFlag 323 324 # Most (all?) of the copy commands in windows blightly assume 325 # that you really ment to put "\*" on the end of source directory 326 # If the user would really like to back that assumption out 327 # we allow him to code an option in the config(cmd,dircp) 328 # (second entry in the list) to tell us what s/he really 329 # wants. To work this wonder we just add the final dir in 330 # the source to the destination... 331 # Uh. that is the default with the new copy code so 332 # negate the test... 333 if {![string match {*-\**} $dirFlag]} { 334 # oops! that must be part of the command 335 set cpcmd [list {*}$cpcmd {*}$dirFlag] 336 # To restore the 'normal' windows type behavior.. 337 # only if the tail is the same, else it is a copy 'as' 338 if {[file tail $ffile] == [file tail $dest]} { 339 set dest [file norm $dest/..] 340 } 341 } 342 if {$cpcmd == {}} { 343 if {$option == "cp"} { 344 set cpcmd "frFileCopy" 345 } else { 346 set cpcmd "recHardLink %b -L" 347 } 348 } else { 349 set cpcmd [list exec {*}[subst {*}$::stOps $cpcmd]] 350 # if {$glob(async)} { 351 # append cpcmd " %b &" 352 # } 353 } 354 } 355 frputs ffile dest 356 frECF [list {*}$cpcmd]\ 357 [list $ffile $dest] 358 # [list -b $glob(async)] 359 } 360 2 { 361 # This is local to VFS file copy 362 Try {VFSputFile $dest $ffile $size } "" 1 363 } 364 3 { 365 # This is local to VFS dir copy 366 Try { CopyToVFSRecursive $ffile $dest} "" 1 367 } 368 4 { 369 # This VFS to local file copy 370 frECF [list VFSgetFile] \ 371 [list $ffile\ 372 $dest \ 373 $size \ 374 $resume ]\ 375 [list -default { "%n"}] 376 # -back $glob(async)] 377 } 378 5 { 379 # This is VFS to local dir copy 380 Try {CopyFromVFSRecursive "$ffile" "$dest" $resume } "" 1 381 # NewPwd $inst $olddir 382 } 383 7 - 384 6 { 385 # This is VFS to VFS file copy 386 if {$option != "cp"} { 387 CantDoThat 388 } 389 # If the two VFS's are the same, try the internal copy 390 set VFSsourceTok $VFStok 391 IsVFS $dest 392 set eh {} 393 if {$VFSsourceTok == $VFStok && \ 394 [catch {VFScopy $VFStok $ffile $dest} eh] == 0} { 395 continue 396 # frputs eh 397 if {![string match {*does not support*} $eh] && $eh != {}} { 398 PopError $eh 399 continue 400 } 401 } 402 # so that failed, try this 403 if {$sw == 6} { 404 # Its a file... 405 Try {VFSputFile $dest [set tmp [MoveToTmp $ffile $type $size]] $size} "" 1 406 file delete -force $tmp 407 } else { 408 # A dir copy 409 set tail [file tail $dest] 410 if {![Try {CopyFromVFSRecursive $ffile [makeTmp]/$tail $resume} "" 1]} { 411 Try {CopyToVFSRecursive [set tmp [makeTmp]/$tail/] $dest} "" 1 412 file delete -force $tmp 413 } 414 } 415 } 416 } 417 } 418 UpdateIf $dest $dest/x 419 return 1 420} 421 422# # This function tests for existance of the file. This is simple for 423# # local files, but a bit complicated for VFS files. We use 'inst' to 424# # select the opposite in case we need to set up a new list. 425# # 426# proc fileExists {file inst} { 427# global glob 428# # do the simple stuff first... 429# if {![IsVFS $file]} { 430# return [file exists $file] 431# } 432# # Lets assume the most difficult... 433# # find the dir it is listed in.. 434# set dir [URL norm $file/..] 435# set pan [Opposite $inst] 436# set savedPwd $glob($pan,pwd) 437# # short cut possible? 438# if {$dir != $savedPwd} { 439# NewPwd $pan $dir 440# UpdateWindow $pan 441# } 442# set ret [lsearch -exact -index 1 $glob($pan,filelist) [file tail $VFSpath]] 443# if {$glob($pan,pwd) != $savedPwd} { 444# NewPwd $pan $savedPwd 445# UpdateWindow $pan 446# } 447# return [expr {$ret == -1 ? 0 : 1}] 448# } 449 450proc CmdCopy {{resume 0} {cmd cp} {as 0}} { 451 global glob 452 CmdCopy_ $glob(listbox,$glob(selected)).file glob($glob(selected),filelist) \ 453 $glob($glob(selected),pwd) $glob(selected) $as $resume $cmd 454 # frputs 455 ForceUpdate both 456} 457proc CmdHardlnk {{resume 0}} { 458 CmdCopy $resume hardlink 459} 460 461proc CmdHardlnkAs {} { 462 CmdCopy 0 hardlink 1 463} 464 465proc CmdCopyAs {} { 466 CmdCopy 0 cp 1 467} 468 469proc CmdCopy_ { listb_name filelist_var frompwd inst as resume option} { 470 global config glob 471 upvar $filelist_var filelist 472 set selList [$listb_name curselection] 473 set fileList {} 474 foreach sel $selList { 475 lassign [lindex $filelist $sel] s file type size 476 set ffile [DNtoDirTail [URL norm $frompwd/$file]] 477 lappend fileList [list $ffile $type $size] 478 } 479 CopyCore $fileList $glob([Opposite $inst],pwd) $inst $as $resume $option 480} 481 482 483proc SoftLink { src dst } { 484 global config 485 if {$config(create_relative_links)} { 486 set srcdir [file dirname $src] 487 set dstdir [file dirname $dst] 488 set srcfile [file tail $src] 489 set dstfile [file tail $dst] 490 set dstlist [split $dstdir /] 491 set srclist [split $srcdir /] 492 set dstlen [llength $dstlist] 493 set srclen [llength $srclist] 494 # Count how many directories are the same in the source and destination paths 495 set index 0 496 while {([lindex $srclist $index] == [lindex $dstlist $index]) && \ 497 ($index < $srclen) && ($index < $dstlen)} { 498 incr index 499 } 500 # Build relative link 501 set link {} 502 for {set dstindex $index} {$dstindex < $dstlen} {incr dstindex} { 503 append link ../ 504 } 505 for {set srcindex $index} {$srcindex < $srclen} {incr srcindex} { 506 append link [lindex $srclist $srcindex]/ 507 } 508 set from $link$srcfile 509 cd $dstdir 510 } else { 511 set from $src 512 } 513 # Log [_ "%s : file -s %s" \ 514 # "$src $dst $srcdir $srcfile $dstdir $dstfile" "$dst $from"] 515 # for MSW the tcl file command seems to put garbage in the link... 516 # We create junctions as we know how to delete them... 517 # Tcl bug c8d4f01a54d7ecf819f4881fb02467ad14fc7b0c 518 set cmd "file link -symbolic" 519 if {$::MSW && [set nc [auto_execok mklink]] !={} } { 520 set cmd "exec $nc /j " 521 } 522 frECF $cmd [list $dst $from] 523 # if {[Try {{*}$cmd $dst $from} "" 0] == 1 } { 524 # Try {file link $dst $from} "" 1 525 # } 526} 527 528proc CmdSoftLink {} { 529 global glob 530 if { $glob(left,pwd) == $glob(right,pwd) } { 531 SandDsame 532 return {} 533 } 534 # Well, with VFS we actually can, but we need them both to be 535 # on the same remote 536 if {([IsVFS $glob(left,pwd)] && [set lVFStok $VFStok] != {}) ||\ 537 [IsVFS $glob(right,pwd)]} { 538 if {$lVFStok == $VFStok} { 539 # missing code here... 540 # It is OK, it is handled below.. 541 } else { 542 frputs "softlink " l r rVFStok lVFStok glob(left,pwd) glob(right,pwd) 543 CantDoThat 544 return 545 } 546 } 547 548 CmdSoftLink_ $glob(listbox,left).file glob(left,filelist) $glob(left,pwd) $glob(right,pwd) 549 CmdSoftLink_ $glob(listbox,right).file glob(right,filelist) $glob(right,pwd) $glob(left,pwd) 550 UpdateWindow both 551} 552 553proc CmdSoftLink_ { listb_name filelist_var frompwd topwd } { 554 # For speed, (hopefully) we'll call by reference... 555 global config glob 556 upvar $filelist_var filelist 557 558 foreach sel [$listb_name curselection] { 559 if {[CheckAbort [_ "SoftLink"]]} {return} 560 set elem [lindex $filelist $sel] 561 set file [lindex $elem 1] 562 set realName [DNtoDirTail [URL norm $frompwd/$file]] 563 switch [lindex $elem 2] { 564 n - 565 d - 566 l - 567 wl - 568 wld - 569 ld { 570 Log [_ "Softlinking %s to %s" "$realName" $topwd] 571 SoftLink $realName $topwd/$file 572 } 573 fn - 574 fd - 575 fl - 576 fld { 577 Log [_ "Softlinking %s to %s" "$realName" $topwd] 578 IsVFS $topwd/$file 579 set opt [expr {$config(create_relative_links) ? "r" : "a"}] 580 Try {VFSlink $realName $VFSpath $opt} "" 1 581 set glob(forceupdate) 1 582 } 583 default { 584 CantDoThat 585 return 586 } 587 } 588 } 589} 590 591proc CmdSoftLinkAs {} { 592 global glob 593 CmdSoftLinkAs_ $glob(listbox,left).file glob(left,filelist) $glob(left,pwd) $glob(right,pwd) 594 CmdSoftLinkAs_ $glob(listbox,right).file glob(right,filelist) $glob(right,pwd) $glob(left,pwd) 595 UpdateWindow both 596} 597 598proc CmdSoftLinkAs_ { listb_name filelist_var frompwd topwd } { 599 # For speed, (hopefully) we'll call by reference... 600 global config glob 601 upvar $filelist_var filelist 602 603 foreach sel [$listb_name curselection] { 604 if {[CheckAbort [_ "SoftLinkAs"]]} {return} 605 set elem [lindex $filelist $sel] 606 set file [lindex $elem 1] 607 set realName [DNtoDirTail [URL norm $frompwd/$file]] 608 set destfile [simple_smart_dialog "." [_ "Soft-Link As..."] \ 609 [_ "Please enter new name for destination link"] \ 610 $topwd/$file ] 611 if {([IsVFS $frompwd] && [set fVFStok $VFStok] !={}) || [IsVFS $destfile]} { 612 # Well, with SVFS we actually can, but we need them both to be 613 # on the same remote & SVFS (but I said that) 614 if {$fVFStok == $VFStok } { 615 } else { 616 CantDoThat 617 return 618 } 619 } 620 if {$destfile != "" } { 621 Log [_ "Softlinking %s to %s" "$realName" $destfile] 622 switch [lindex $elem 2] { 623 n - 624 d - 625 l - 626 wl - 627 wld - 628 ld { 629 SoftLink $realName $destfile 630 } 631 fn - 632 fd - 633 fl - 634 fld { 635 Log [_ "Softlinking %s to %s" "$realName" $destfile] 636 # This sets up VFSadd to be VFSadd 637 IsVFS $destfile 638 set opt [expr {$config(create_relative_links) ? "r" : "a"}] 639 Try {VFSlink $realName \ 640 "$VFSpath" \ 641 "$opt"} "" 1 642 set glob(forceupdate) 1 643 } 644 default {CantDoThat} 645 } 646 } 647 } 648} 649 650# This handles all deletes. For locals we use the configured delete 651# We do the async option with override on the third parm 652 653proc deleteFile {args} { 654 global config glob 655 # if we are passed a list, the args processing will have listed it again 656 # so, if the length is 1, that sublist is what we want 657 if {[llength $args] == 1} { 658 set args [lindex $args 0] 659 } 660 foreach {newfile tType} $args { 661 frputs tType newfile args 662 # tType should be f for files d for dirs 663 switch -exact -- $tType[IsVFS $newfile] { 664 f0 - 665 d0 { 666 if {[catch {expr {[file exists $newfile] || \ 667 [file type $newfile] == "link"}} r] == 0 && $r} { 668 if {$config(cmd,rm) != {}} { 669 # assume a recycle thing 670 Log [_ "Using \"%s\" to delete %s" $config(cmd,rm) $newfile ] 671 Try { {*}$config(cmd,rm) $newfile} "-sPossibly open file:" 672 } else { 673 Log [_ "Deleting %s" $newfile] 674 Try {file delete -force -- $newfile} "-sPossibly open file:" 675 } 676 } 677 } 678 f1 { 679 Try { VFSdelete $newfile} "-sPossibly open file:" 680 } 681 d1 { 682 Try { DeleteVFSRecursive $newfile} "-sPossibly open file:" 683 } 684 00 - 685 01 {return} 686 default { 687 return -code error "deleteFile called with $tType not in {0 d f}" 688 } 689 } 690 } 691} 692 693# Here we implement a cache of filelists indexed by dir 694# 695proc getFileListFor {dir} { 696 upvar #0 glob(left,pwd) lpwd 697 upvar #0 glob(right,pwd) rpwd 698 global glob 699 global fileListCache fileListExpir 700 701 if {$dir == $lpwd} { 702 return $glob(left,filelist) 703 } elseif {$dir == $rpwd} { 704 return $glob(right,filelist) 705 } elseif {[info exists fileListCache($dir)] && $fileListCache($dir,time) > [clock seconds] + 10} { 706 return $fileListCache($dir) 707 } else { 708 catch {after cancel $fileListExpir} 709 set fileListCache($dir,time) [clock seconds] 710 set fileListExpir [after 10000 {array unset ::fileListCache}] 711 set r [catch {adHockGetDirList $dir} list] 712 if {$r == 0} { 713 return [set fileListCache($dir) $list] 714 } 715 return {} 716 } 717} 718# 719# This routine attempts to determine if the given file exists 720# and if so, its type. Returns are: 721# "d" exists and is directory 722# "f" exists and is file 723# "0" does not exist 724 725proc fileType {file} { 726 global glob 727 upvar #0 glob(left,pwd) lpwd 728 upvar #0 glob(right,pwd) rpwd 729 730 if {![IsVFS $file]} { 731 set dir [file dir $file] 732 if {[file isdir $file]} {return "d"} 733 if {[file isfile $file]} {return "f"} 734 return 0 735 } 736 737 set dir [URL dir $file] 738 set sObj [dirToDNexact $file] 739 set sObj [expr {$sObj == {} ? [file tail $file] : $sObj}] 740 741 set ent [lsearch -exact -inline -index 1 [getFileListFor $dir] $sObj] 742 if {$ent == {}} {return 0} 743 return [expr {[string index [lindex $ent 2] end] == "d" ? "d" : "f"}] 744} 745 746# Move and rename are, at heart, the same thing. Especially if we 747# bring moveas into the mix. Here are the three cases: 748# 749# 1.) move just a simple move, target must differ from source 750# (seed will be Move} 751# 2.) moveas the same as rename except for the seed in the prompt 752# 3.) rename just a different seed in the prompt 753# 754 755proc CmdMove {{seed {Move}}} { 756 global config glob 757 upvar #0 glob(selected) inst 758 upvar #0 glob(listbox,$inst) listb_name 759 upvar #0 glob($inst,filelist) filelist 760 upvar #0 glob($inst,pwd) frompwd 761 upvar #0 glob([Opposite $inst],pwd) topwd 762 763 set selList [$listb_name.file curselection] 764 # This keeps track of global yes/no from the ok to delete query 765 # Move does not delete dirs, but it may delete files... 766 # 2 global yes 767 # 3 global no 768 # Other vaues -> unknown 769 set globalYNfile 0 770 set globalYNdir 0 771 772 # A note on async. We need to grab all the selected file names 773 # before we return. For multi-step operations, we need to 774 # serialize the steps... 775 set moveFiles {} 776 foreach sel $selList { 777 if {[CheckAbort [_ "Move"]]} {return} 778 set elem [lindex $filelist $sel] 779 lassign $elem duh file type size 780 set realName [DNtoDirTail [URL norm $frompwd/$file]] 781 set toRealName [DNtoDirTail [URL norm $topwd/$file]] 782 783 set tof $topwd/$file 784 if {$seed != "Move"} { 785 # a rename or move to a new name (moveas) 786 787 set newname [simple_smart_dialog "." \ 788 [_ "%s" $seed] \ 789 [_ "Please enter new name."] \ 790 [expr {$seed == "Rename" ? $realName : $toRealName}]] 791 if {$newname == ""} {continue} 792 } else { 793 set newname $toRealName 794 } 795 # The rules, what is and what is not ok. 796 # Rename or Move to the same name is NOT ok. 797 # this may happen even if its a simple move. 798 # Then there is the MSW alias issue (i.e. same except for case). 799 # We want to allow this to effectively change the case of a name. 800 if {$newname == $realName} { 801 SandDsame 802 return 803 } 804 set tType [fileType $newname] 805 frputs tType newname 806 if {$tType == "f" &&\ 807 (!$::MSW || ![string equal -nocase $newname $realName])} { 808 # We have an implied delete 809 # set up the permission code... 810 set dT {} 811 set ty "file" 812 set ford [expr {$config(ask,file_delete) ? [_ "file"] : {}}] 813 # frputs tType type 814 if {![typeMatch $tType $newname $type $realName]} { 815 return 816 } 817 } 818 # We don't check ownership on VFS files. (We don't usually know 819 # who the owner is in this case.) Of course CheckWhoOwns doesn't 820 # know how to approach VFS (yet??) 821 822 if {([IsVFS $realName] || [CheckWhoOwns $realName $seed]) && \ 823 ($tType == "d" || \ 824 [IsVFS $newname] ||\ 825 [CheckWhoOwns $newname overwrite])} { 826 827 # This keeps track of global yes/no from the ok to delete query 828 # 2 global yes 829 # 3 global no 830 # Other vaues -> unknown 831 # deleteQuery returns 0 -> Yes, 1 -> No, 2-> abort, 832 # 3 ->Yes to all, 4 ->No to all 833 # Only ask about existent files 834 # Since we are doing this one at a time and we don't know if there 835 # is more than one existant file, the length of selList is a conserative 836 # proxy for the number of files (i.e. always more than or =) 837 if {$tType == "f"} { 838 switch -exact [set globalYN$ty] { 839 4 {continue} 840 0 - 841 1 {set globalYN$ty [deleteQuery $ford $newname [llength $selList] [_ $seed]]} 842 } 843 switch -exact [set globalYN$ty] { 844 1 - 845 4 {continue} 846 2 {return 0} 847 } 848 } 849 850 } else { 851 continue 852 } 853 854 855 # Ok, things are looking serious. We have a few issues left 856 # 1) If we are doing a moveas and it is a directory, well 857 # there is no command to do that. For the local case 858 # rename does the trick. 859 # For all others we need to do a copyas followed by a 860 # delete. 861 862 863 # Here is where we start doing things.... first the locals 864 lappend UpD $realName $realName/x $newname $newname/x 865 if {![IsVFS $newname] && ![IsVFS $realName]} { 866 867 # If we have an implied delete, make it explicit 868 # and use the configured rm command 869 if {$tType == "f"} { 870 deleteFile $newname $tType 871 } 872 Log [_ "Moving %s to %s" $realName $newname] 873 # Note in passing, "file rename" moves the file if 874 # required (man page says a different directory, hope 875 # it means a different volume) 876 Try { file rename -force -- $realName $newname } 877 continue 878 } 879 # Its remote (on one or both sides) 880 # collect what we need so we can do the whole thing as one big async 881 # 882 lappend moveFiles [list $realName $newname $type $tType $size] 883 } 884 # At this time we have all the info we need in the moveFiles list 885 # lets do this either async or not as requested. 886 if {$moveFiles != {}} { 887 Try {moveDoIt $moveFiles} $glob(async) 888 } 889 UpdateIf {*}$UpD 890} 891 892proc moveDoIt {moveFiles } { 893 foreach ent $moveFiles { 894 frputs ent moveFiles 895 if {[CheckAbort [_ "Move"]]} {break} 896 lassign $ent realName newname type tType size 897 lappend UpL $realName $realName/x $newname $newname/x 898 899 if {[set rf [IsVFS $realName]]} { 900 set VFStokf $VFStok 901 } 902 if {[IsVFS $newname] &&\ 903 $rf &&\ 904 $VFStokf == $VFStok && \ 905 [VFSsupports $newname "rename"]} { 906 # both VFS... and the same and rename supported... 907 frputs realName VFSpath newname 908 Try { VFSrename $realName $VFSpath\ 909 [expr {[string index $type end] == "d"}]} "" 1 910 continue 911 } 912 # Ok rename is out, we need to do a move and a delete. 913 # one or both VFS but if both, not the same (or rename not supported) 914 # six cases: 915 # VFS to VFS file VFStoVFSf 916 # VFS to VFS directory VFStoVFSd 917 # local to VFS file loctoVFSf 918 # local to VFS directory loctoVFSd 919 # VFS to local file VFStolocf 920 # VFS to local directory VFStolocd 921 # 922 # Also, if dest exists we need to deleteit 923 924 if {$tType == "f"} { 925 deleteFile $newname $tType 926 } 927 set resume 0 928 set sType [expr {[string index $type end] == "d" ? "d" : "f"}] 929 set whichCase [expr {$rf ? "VFS" : "loc"}]to[expr {[IsVFS $newname] ? \ 930 "VFS" : "loc"}]$sType 931 set tmpLoc {} 932 Log [_ "Move %s to %s" $realName $newname] 933 934 switch -exact $whichCase { 935 936 VFStoVFSf { 937 # VFS to VFS, copy to local then to dest. 938 # This VFS to local file copy 939 if {[set tmpLoc [MoveToTmp $realName $type $size]] == 0} { 940 incr er 941 } elseif {[Try {VFSputFile $newname $tmpLoc $size } "" 1] != 0} { 942 incr er 943 } 944 } 945 VFStoVFSd { 946 set tmpLoc [makeTmp]/[file tail $realName] 947 if {[Try {CopyFromVFSRecursive "$realName" "$tmpLoc" $resume }] != 0} { 948 Try {file delete -force -- $tmpLoc} "" 1 949 } elseif {[Try {CopyToVFSRecursive $tmpLoc $newname}] != 0} { 950 incr er 951 } 952 } 953 loctoVFSf { 954 if {[Try {VFSputFile $newname $realName $size }] != 0} { 955 incr er 956 } 957 } 958 loctoVFSd { 959 if {[Try {CopyToVFSRecursive $realName $newname}] != 0} { 960 incr er 961 } 962 } 963 VFStolocf { 964 # This VFS to local file copy 965 if {$type == "fl"} {set size -1} 966 if {[Try {frECF [list VFSgetFile] \ 967 [list $realName\ 968 $newname \ 969 $size \ 970 $resume]\ 971 } "" 1] != 0} { 972 incr er 973 } 974 } 975 VFStolocd { 976 # This is VFS to local dir copy 977 if {[Try {CopyFromVFSRecursive "$realName" "$newname" $resume}] != 0} { 978 incr er 979 } 980 } 981 default {return -code error "Impossible file move $whichCase"} 982 } 983 # only do the delete if no er 984 if {![info exists er]} { 985 Log [_ "Deleting %s" $realName] 986 if {[IsVFS $realName]} { 987 if {$sType == "d"} { 988 Try {DeleteVFSRecursive $realName} 989 } else { 990 Try {VFSdelete $realName} "" 1 991 } 992 } else { 993 Try {file delete -force -- $realName} 994 } 995 } 996 if {$tmpLoc != {}} { 997 Try {file delete -force -- $tmpLoc} 998 } 999 } 1000 UpdateIf {*}$UpL 1001} 1002 1003 1004proc CmdMoveAs {} { 1005 CmdMove 1 1006} 1007 1008 1009 1010proc CmdDelete {} { 1011 global glob 1012 CmdDelete_ $glob(listbox,left).file glob(left,filelist) $glob(left,pwd) $glob(right,pwd) left 1013 CmdDelete_ $glob(listbox,right).file glob(right,filelist) $glob(right,pwd) $glob(left,pwd) right 1014 UpdateWindow both 1015 set glob(forceupdate) 0 1016} 1017# deleteQuery asks if it is ok to delete something. It puts up a window 1018# with the message: "OK to delete 'ford' 'elem'" and button answers: 1019# 'Yes' 0, 'No' 1, Abort 'op' 2, Yes to All 3, No to All 4. 1020# The last two buttons will be displayed only if 'num' is greater than 1 1021# 'ford' was set up to read directory or file. X deletion will return 2. 1022 1023proc deleteQuery {ford elem num op} { 1024 if {$ford == ""} {return 0} 1025 set ret [smart_dialog .apop[incr ::uni] . [_ "Sure?"] \ 1026 [list [_ "OK to delete %s ?" \ 1027 "$ford $elem"]]\ 1028 0 [expr {$num > 1 ? 5 : 3}] \ 1029 [list [_ "Yes"] [_ "No"] [_ "Abort %s" $op] \ 1030 [_ "Yes to All"] [_ "No to All"]]] 1031 if {$ret == -1} {return 2} 1032 return $ret 1033} 1034 1035proc CmdDelete_ { listb_name filelist_var frompwd topwd inst } { 1036 # For speed, (hopefully) we'll call by reference... 1037 global config glob 1038 upvar $filelist_var filelist 1039 1040 set selList [$listb_name curselection] 1041 # This keeps track of global yes/no from the ok to delete query 1042 # 3 global yes 1043 # 4 global no 1044 # Other vaues -> unknown (0 yes, 1 no) 1045 # we ask seperatly for files and dirs. 1046 set dglbYN 0 1047 set glbYN 0 1048 # We want to allow async, but need to pull all the info from the 1049 # dirlist before we do, thus we build a work list and then do 1050 # the work with Try, possibly asynchronously 1051 set delList {} 1052 foreach sel $selList { 1053 if {[CheckAbort [_ "Delete"]]} return 1054 set elem [lindex $filelist $sel] 1055 set type [lindex $elem 2] 1056 set ford [switch $type { 1057 d - 1058 fd {if {$config(ask,dir_delete)} {expr {{directory tree}}}} 1059 default {if {$config(ask,file_delete)} {expr {{file}}}} 1060 }] 1061 set dT [expr {$ford != "file" ? {d} :{}}] 1062 1063 set ff [DNtoDirTail [URL norm $frompwd/[lindex $elem 1]]] 1064 switch -exact [set ${dT}glbYN] { 1065 4 {continue} 1066 0 - 1067 1 {set ${dT}glbYN [deleteQuery $ford $ff [llength $selList] [_ "Delete"]]} 1068 } 1069 switch -exact [set ${dT}glbYN] { 1070 1 - 1071 4 {continue} 1072 2 {return} 1073 } 1074 set tType [expr {[string index $type end] == "d" ? "d" : "f"}] 1075 switch $type { 1076 l - 1077 wl - 1078 ld - 1079 wld - 1080 n - 1081 d { 1082 if {[CheckWhoOwns $ff delete]} { 1083 lappend delList $ff $tType 1084 } 1085 } 1086 fn - 1087 fld - 1088 fd - 1089 fl { 1090 Log [_ "Deleting %s" $ff] 1091 lappend delList $ff $tType 1092 } 1093 default CantDoThat 1094 } 1095 } 1096 if {$delList == {}} {return} 1097 Try { 1098 deleteFile $delList {;} 1099 UpdateIf $ff 1100 } "-sPossibly open file:" $::glob(async) 1101} 1102# This command is nearly the same as View It uses the windows 1103# file 'assoc' and 'ftype' functions to figure out how 1104# to 'open' a file. (We use 'open' as windows means it.) 1105 1106 1107proc CmdOpen {} { 1108# puts "CmdWinOpen" 1109 global glob 1110 CmdView_ $glob(listbox,left).file glob(left,filelist) \ 1111 $glob(left,pwd) $glob(right,pwd) left open 1112 CmdView_ $glob(listbox,right).file glob(right,filelist) \ 1113 $glob(right,pwd) $glob(left,pwd) right open 1114} 1115 1116# This command allows the help file to be compressed, .gz now, .zip someday. 1117 1118proc ViewHelp {seed} { 1119 frputs "ViewHelp " seed 1120 if {[file exists $seed]} { 1121 ViewText $seed 1122 } elseif {[file exists $seed.gz]} { 1123 ViewText $seed.gz 1124 } else { 1125 PopError "Can't find $seed" 1126 } 1127} 1128 1129proc CmdView {} { 1130# puts "CmdView" 1131 global glob 1132 CmdView_ $glob(listbox,left).file glob(left,filelist) \ 1133 $glob(left,pwd) $glob(right,pwd) left 1134 CmdView_ $glob(listbox,right).file glob(right,filelist) \ 1135 $glob(right,pwd) $glob(left,pwd) right 1136} 1137 1138proc CmdView_ { listb_name filelist_var frompwd topwd inst {extList view}} { 1139 # For speed, (hopefully) we'll call by reference... 1140 # puts "CmdView_" 1141 global glob config env 1142 upvar $filelist_var filelist 1143 set filenamelist {} 1144 set filenameorg {} 1145 foreach sel [$listb_name curselection] { 1146 if {[CheckAbort [_ "View"]]} return 1147 set fileelem [lindex $filelist $sel] 1148 set file1 [DNtoDirTail [URL join $frompwd [lindex $fileelem 1]]] 1149 frputs file1 frompwd 1150 # for windows lnk files we pass both the translated version and the file 1151 set type [lindex $fileelem 2] 1152 switch $type { 1153 l - 1154 n { 1155 lappend filenamelist $file1 1156 lappend filenameorg $file1 1157 } 1158 wl { 1159 lappend filenamelist [TranslateLnk [wLinkName $inst $fileelem] \ 1160 [lindex $glob($inst,df) 1]] 1161 lappend filenameorg $file1 1162 } 1163 wld { 1164 set newdir [TranslateLnk [wLinkName $inst $fileelem] \ 1165 [lindex $glob($inst,df) 1]] 1166 if {$newdir != {}} { 1167 NewPwd $inst $newdir 1168 UpdateWindow $inst 1169 } 1170 } 1171 fd - 1172 fld - 1173 ld - 1174 d { 1175 NewPwd $inst $file1 1176 UpdateWindow $inst 1177 return 1178 } 1179 fn - 1180 fl { 1181 set file [MoveToTmp $file1 $type [lindex $fileelem 3]] 1182 lappend filenamelist $file 1183 lappend filenameorg $file1 1184 } 1185 default CantDoThat 1186 } 1187 } 1188 if {$filenamelist != {}} { 1189 ViewAny $filenamelist $extList $filenameorg 1190 } 1191} 1192 1193proc makeTmp {} { 1194 global glob 1195 set r 0 1196 if { ! [file exists $glob(tmpdir)] } { 1197 set r [Try { file mkdir $glob(tmpdir) } "" 1] 1198 if {$r} { 1199 return -code error "Failed to create $glob(tmpdir)." 1200 } 1201 } 1202 return $glob(tmpdir) 1203} 1204 1205proc MoveToTmp {file type size args} { 1206 global glob 1207 1208 set dest [makeTmp]/[file tail $file] 1209 if {$args != {}} { 1210 set inc {} 1211 while {[file exists $dest$inc]} {set inc [expr {$inc == {} ? "0" : [incr inc]}]} 1212 set dest $dest$inc 1213 } else { 1214 file delete -force $dest 1215 } 1216 #set size [lindex $fileelem 3] 1217 if {$type == "fl"} {set size -1} 1218 set rr [frECF [list VFSgetFile %b $size 0]\ 1219 [list $file $dest]] 1220 lassign $rr r val cm 1221 1222 if {$r == 0} { 1223 set glob(havedoneftp) 1 1224 # puts "$glob(tmpdir)/[lindex $fileelem 1]" 1225 #return [regsub -all {\ }\ 1226 # "$glob(tmpdir)/[lindex $fileelem 1]" {\\ }] 1227 return $dest 1228 } 1229 PopError "Command $cm \ncaused error: $val" 1230 return 0 1231} 1232 1233 1234 1235# proc CmdViewAsText {} { 1236# global glob 1237# CmdViewAsText_ $glob(listbox,left).file glob(left,filelist) \ 1238# $glob(left,pwd) $glob(right,pwd) 1239# CmdViewAsText_ $glob(listbox,right).file glob(right,filelist) \ 1240# $glob(right,pwd) $glob(left,pwd) 1241# }listb_name filelist_var frompwd topwd 1242 1243proc CmdViewAsText { } { 1244 global glob 1245 set inst $glob(selected) 1246 set listb_name $glob(listbox,$inst).file 1247 set filelist $glob($inst,filelist) 1248 set frompwd $glob($inst,pwd) 1249 # For speed, (hopefully) we'll call by reference... 1250 # upvar $filelist_var filelist 1251 # set inst $glob(select_cur_lr) 1252 foreach sel [$listb_name curselection] { 1253 lassign [lindex $filelist $sel] dum file type size 1254 set ffile [DNtoDirTail [URL norm $frompwd/$file]] 1255 switch $type { 1256 wl { ViewText [TranslateLnk [wLinkName $inst [lindex $filelist $sel]] \ 1257 [lindex $glob($inst,df) 1]] 1258 } 1259 l - 1260 n { ViewText $frompwd/$file } 1261 ld - 1262 wld - 1263 d { PopInfo [_ "Can't view directory %s in the text viewer" \ 1264 "$ffile"] } 1265 fn - 1266 fl { ViewText [MoveToTmp $ffile $type $size] $ffile} 1267 default CantDoThat 1268 } 1269 } 1270} 1271 1272proc CmdCheckSize {} { 1273 global glob 1274 CmdCheckSize_ $glob(listbox,left).file glob(left,filelist)\ 1275 $glob(left,pwd) $glob(right,pwd) left 1276 CmdCheckSize_ $glob(listbox,right).file glob(right,filelist) \ 1277 $glob(right,pwd) $glob(left,pwd) right 1278} 1279 1280proc CmdCheckSize_ { listb_name filelist_var frompwd topwd inst } { 1281 # For speed, (hopefully) we'll call by reference... 1282 global glob config env 1283 upvar $filelist_var filelist 1284 set filenamelist {} 1285 foreach sel [$listb_name curselection] { 1286 if {[CheckAbort [_ "View"]]} return 1287 set fileelem [lindex $filelist $sel] 1288 switch [lindex $fileelem 2] { 1289 d - 1290 ld - 1291 wld - 1292 wl - 1293 l - 1294 n { lappend filenamelist [DNtoDirTail [URL norm $frompwd/[lindex $fileelem 1]]] } 1295 default CantDoThat 1296 } 1297 } 1298 if {$filenamelist != {}} { 1299 set pr [lindex $config(cmd,du) 0] 1300 frECF [list exec {*}[subst {*}$::stOps $config(cmd,du)]] $filenamelist \ 1301 [list -post \ 1302 [list postOptions [list ViewString [_ "Output from %s" $pr]] nop ]] 1303 } 1304} 1305 1306 1307proc CmdWhatIs {} { 1308 global glob 1309 CmdWhatIs_ $glob(listbox,left).file glob(left,filelist)\ 1310 $glob(left,pwd) $glob(right,pwd) 1311 CmdWhatIs_ $glob(listbox,right).file glob(right,filelist)\ 1312 $glob(right,pwd) $glob(left,pwd) 1313} 1314 1315proc CmdWhatIs_ { listb_name filelist_var frompwd topwd } { 1316 # For speed, (hopefully) we'll call by reference... 1317 upvar $filelist_var filelist 1318 set files {} 1319 foreach sel [$listb_name curselection] { 1320 lassign [lindex $filelist $sel] dum file type 1321 switch $type { 1322 l - 1323 wl - 1324 wld - 1325 n - 1326 ld - 1327 d {lappend files [DNtoDirTail [URL norm $frompwd/$file]] 1328 } 1329 default CantDoThat 1330 } 1331 } 1332 if {$files != {}} { 1333 frECF "exec file %b" $files\ 1334 [list -post [list postOptions [list ViewString [_ "Output from file"]] nop ]] 1335 } 1336} 1337 # Try { PopInfo [exec file "$frompwd/$file"] } "" 1 1338 1339proc CmdEdit {} { 1340 global glob 1341 CmdEdit_ $glob(listbox,left).file glob(left,filelist) \ 1342 $glob(left,pwd) $glob(right,pwd) 1343 CmdEdit_ $glob(listbox,right).file glob(right,filelist) \ 1344 $glob(right,pwd) $glob(left,pwd) 1345} 1346 1347proc CmdEdit_ { listb_name filelist_var frompwd topwd } { 1348 global env config glob 1349 # For speed, (hopefully) we'll call by reference... 1350 upvar $filelist_var filelist 1351 set filenamelist {} 1352 set inst $glob(select_cur_lr) 1353 1354 foreach sel [$listb_name curselection] { 1355 set elem [lindex $filelist $sel] 1356 lassign $elem dum file swt 1357 # set swt [lindex $elem 2] 1358 switch -glob $swt { 1359 wl* { 1360 set ffile [TranslateLnk [wLinkName $inst $elem] \ 1361 [lindex $glob($inst,df) 1]] 1362 } 1363 default {set ffile [DNtoDirTail [URL norm $frompwd/$file]]} 1364 } 1365 switch $swt { 1366 l - 1367 wl - 1368 n { lappend filenamelist $ffile } 1369 ld - 1370 wld - 1371 d { PopInfo [_ "Can't edit directory %s in the text editor" "$ffile"] } 1372 default CantDoThat 1373 } 1374 } 1375 if {$filenamelist != {}} { 1376 Log "exec $config(editor) $filenamelist" 1377 1378 set rr [frECF [list exec {*}[subst {*}$::stOps $config(editor)]] $filenamelist] 1379 } 1380} 1381 1382proc DoUsrButton {which} { 1383 global config glob 1384 upvar #0 glob(selected) inst 1385 upvar #0 glob(listbox,$inst) listb_name 1386 upvar #0 glob($inst,filelist) filelist 1387 upvar #0 glob($inst,pwd) pwd 1388 # get the config info 1389 set command {} 1390 set name $which 1391 foreach f {dirs nomultiple viewout prior numparms} { 1392 set $f 0 1393 } 1394 foreach {key value} $config(userButton,$which) { 1395 # we look for "n" first to grandfather the nomultiple "n" abbreviation 1396 switch -glob $key { 1397 n - 1398 no* {set nomultiple $value} 1399 nu* {set numparms $value} 1400 l* {set name $value} 1401 c* {set command $value} 1402 d* {set dirs $value} 1403 v* {set viewout $value} 1404 p* {set prior $value} 1405 } 1406 } 1407 # These make sense? 1408 foreach f {dirs nomultiple viewout prior} { 1409 if {[string is boolean [set $f]]} {continue} 1410 PopWarn "Value for $f ($value) is not boolean." 1411 return 1412 } 1413 if {$nomultiple && ($prior || $numparms !=0)} { 1414 PopWarn "It is inconsistent to assert both \"nomultiple\" and \"prior\" or \"numparms\"." 1415 } 1416 if {$nomultiple} { 1417 set numparms "== 1" 1418 } 1419 if {$numparms == 0} { 1420 set numparms "*" 1421 } 1422 if {[string is integer $numparms]} { 1423 set numparms "==$numparms" 1424 } 1425 # get the current selected file list... 1426 set fileList {} 1427 # if doing prior, collect that first to maintain order 1428 set opwd $glob([Opposite $inst],pwd) 1429 if {$prior && [set pinst $glob(select_pry_lr)] != {}} { 1430 set opwd $glob($pinst,pwd) 1431 foreach sel $glob(select_pry_s) { 1432 set elem [lindex $glob($pinst,filelist) $sel] 1433 lassign $elem du file type 1434 if {[string match "wl*" $type]} { 1435 set file [TranslateLnk [wLinkName $pinst $elem] \ 1436 [lindex $glob($pinst,df) 1]] 1437 } 1438 lappend fileList [list [DNtoDirTail [URL norm $opwd/$file]] $type] 1439 } 1440 } 1441 1442 foreach sel [$listb_name.file curselection] { 1443 set elem [lindex $filelist $sel] 1444 lassign $elem duh file type 1445 if {[string match "wl*" $type]} { 1446 set file [TranslateLnk [wLinkName $inst $elem] \ 1447 [lindex $glob($inst,df) 1]] 1448 } 1449 lappend fileList [list [DNtoDirTail [URL norm $pwd/$file]] $type] 1450 } 1451 # Ok we now hav a list of all the files. 1452 # At the moment we will not allow VFS files and 1453 # while checking that, verify that any dirs are allowed. 1454 set newFlist {} 1455 # test for proper number of parameters * | < n | > n | == n| 1456 if {$numparms == "*" || [eval expr [llength $fileList] $numparms]} { 1457 } else { 1458 PopWarn "[llength $fileList] parms does not pass $numparms test" 1459 return 1460 } 1461 foreach ent $fileList { 1462 lassign $ent file type 1463 if {!$dirs && [string index $type end] == "d"} { 1464 PopWarn "The file \"$file\" is a directory which is not allowed." 1465 return 1466 } 1467 if {[IsVFS $file]} { 1468 PpWarn "The file \"$file\" is a VFS file which is not allowed." 1469 return 1470 } 1471 lappend newFlist $file 1472 } 1473 # Ok, lets do it... 1474 if {$viewout} { 1475 set post [list -post \ 1476 [list postOptions \ 1477 [list ViewString "$name output: " ]\ 1478 [list PopInfo "No output" ]]] 1479 } else { 1480 set post {} 1481 } 1482 frECF [list exec {*}[subst {*}$::stOps $command]] $newFlist {*}$post \ 1483 [list -error {}] 1484 1485} 1486 1487 1488proc CmdQEdit {} { 1489 global glob 1490 CmdQEdit_ $glob(listbox,left).file glob(left,filelist) \ 1491 $glob(left,pwd) $glob(right,pwd) 1492 CmdQEdit_ $glob(listbox,right).file glob(right,filelist) \ 1493 $glob(right,pwd) $glob(left,pwd) 1494} 1495 1496proc CmdQEdit_ { listb_name filelist_var frompwd topwd } { 1497 # For speed, (hopefully) we'll call by reference... 1498 global glob 1499 upvar $filelist_var filelist 1500 set inst $glob(select_cur_lr) 1501 1502 foreach sel [$listb_name curselection] { 1503 if {[CheckAbort [_ "Q-Edit"]]} return 1504 set elem [lindex $filelist $sel] 1505 lassign $elem dum file swt 1506 # set swt [lindex $elem 2] 1507 switch -glob $swt { 1508 wl* { 1509 set ffile [TranslateLnk [wLinkName $inst $elem] \ 1510 [lindex $glob($inst,df) 1]] 1511 } 1512 default {set ffile [DNtoDirTail [URL norm $frompwd/$file]]} 1513 } 1514 switch $swt { 1515 wl - 1516 l - 1517 n { 1518 set r [Try {EditText "$ffile" ""} \ 1519 [_ "Error editing %s" $ffile] 1] 1520 if {$r != 0} { catch { destroy .toplevel_$glob(toplevelidx) } } 1521 } 1522 ld - 1523 d { PopInfo [_ "Can't edit directory %s in the text editor" "$ffile"] } 1524 default CantDoThat 1525 } 1526 } 1527} 1528 1529 1530proc CmdRename {} { 1531 CmdMove Rename 1532 return 1533} 1534 1535proc CmdUnArc {} { 1536 CmdUnArcPack unarc 1537} 1538proc CmdUnPack {} { 1539 CmdUnArcPack unpack 1540} 1541 1542proc CmdUnArcPack {which} { 1543 global glob 1544 CmdUnArcPack_ $glob(listbox,left).file glob(left,filelist) \ 1545 $glob(left,pwd) $glob(right,pwd) $which 1546 CmdUnArcPack_ $glob(listbox,right).file glob(right,filelist)\ 1547 $glob(right,pwd) $glob(left,pwd) $which 1548 UpdateWindow both 1549} 1550 1551proc CmdUnArcPack_ { listb_name filelist_var frompwd topwd which} { 1552 upvar $filelist_var filelist 1553 global glob 1554 set inst $glob(select_cur_lr) 1555 foreach sel [$listb_name curselection] { 1556 if {[CheckAbort $which]} return 1557 set elem [lindex $filelist $sel] 1558 lassign $elem dum file swt 1559 # set swt [lindex $elem 2] 1560 switch -glob $swt { 1561 wl* { 1562 set ffile [TranslateLnk [wLinkName $inst $elem] \ 1563 [lindex $glob($inst,df) 1]] 1564 } 1565 default {set ffile [DNtoDirTail [URL norm $frompwd/$file]]} 1566 } 1567 switch $swt { 1568 ld - 1569 wld - 1570 d { PopError [_ "Can't %s directory %s..." $which $file] } 1571 l - 1572 wl - 1573 n { Log [_ "Which %s to %s" $ffile $topwd] 1574 UnArcPackAny $ffile $topwd $which 1575 } 1576 default CantDoThat 1577 } 1578 } 1579} 1580 1581 1582proc CmdArc {} { 1583 global glob 1584 CmdArc_ $glob(listbox,left).file glob(left,filelist) \ 1585 $glob(left,pwd) $glob(right,pwd) 1586 CmdArc_ $glob(listbox,right).file glob(right,filelist) \ 1587 $glob(right,pwd) $glob(left,pwd) 1588 UpdateWindow both 1589} 1590 1591proc CmdArc_ { listb_name filelist_var frompwd topwd } { 1592 global config glob 1593 upvar $filelist_var filelist 1594 set inst $glob(select_cur_lr) 1595 1596 foreach sel [$listb_name curselection] { 1597 if {[CheckAbort [_ "Archive"]]} return 1598 set elem [lindex $filelist $sel] 1599 lassign $elem dum file swt 1600 # set swt [lindex $elem 2] 1601 switch -glob $swt { 1602 wl* { 1603 set ffile [TranslateLnk [wLinkName $inst $elem] \ 1604 [lindex $glob($inst,df) 1]] 1605 1606 } 1607 default {set ffile [DNtoDirTail [URL norm $frompwd/$file]]} 1608 } 1609 set tail [file tail $ffile] 1610 set dir [file dirname $ffile] 1611 switch $swt { 1612 wl - 1613 l - 1614 n { 1615 Log [_ "Packing %s" $ffile] 1616# Try { eval exec [format $config(cmd,pack) \ 1617 # {$ffile}] } "" 1 $glob(async) 1618 set rr [frECF [list exec {*}[subst {*}$::stOps $config(cmd,pack)]] \ 1619 [list $ffile] \ 1620 [list -b $glob(async)]] 1621 lassign $rr r out 1622 if {$r != 0} { 1623 PopError $out 1624 } 1625 } 1626 wld - 1627 ld - 1628 d { 1629 Log [_ "Archiving %s" $ffile] 1630 cd $dir 1631 if {$config(cmd,archive) == "tar+gz {%s}"} { 1632 frECF [list exec tar cf - %a | gzip > %a] \ 1633 [list $tail $tail.tar.gz] \ 1634 [list -back $glob(async)] 1635 1636 # Try { cd $dir; exec tar cf - $tail | \ 1637 # gzip > $tail.tar.gz } "" 1 $glob(async) 1638 } else { 1639 frECF [list exec {*}[subst {*}$::stOps $config(cmd,archive)]]\ 1640 [list $ffile]\ 1641 [list -back $glob(async)] 1642 } 1643 } 1644 default CantDoThat 1645 } 1646 } 1647} 1648 1649proc CmdPrint {} { 1650 global glob 1651 CmdPrint_ $glob(listbox,left).file glob(left,filelist) \ 1652 $glob(left,pwd) $glob(right,pwd) 1653 CmdPrint_ $glob(listbox,right).file glob(right,filelist) \ 1654 $glob(right,pwd) $glob(left,pwd) 1655} 1656 1657proc CmdPrint_ { listb_name filelist_var frompwd topwd } { 1658 global config glob 1659 upvar $filelist_var filelist 1660 set inst $glob(select_cur_lr) 1661 1662 foreach sel [$listb_name curselection] { 1663 if {[CheckAbort [_ "Print"]]} return 1664 set elem [lindex $filelist $sel] 1665 lassign $elem dum file swt 1666 # set swt [lindex $elem 2] 1667 switch -glob $swt { 1668 wl* { 1669 set ffile [TranslateLnk [wLinkName $inst $elem] \ 1670 [lindex $glob($inst,df) 1]] 1671 } 1672 default {set ffile [DNtoDirTail [URL norm $frompwd/$file]]} 1673 } 1674 switch $swt { 1675 wl - 1676 l - 1677 n { 1678 Log [_ "Printing %s" $ffile] 1679 frECF [list exec {*}[subst {*}$::stOps $config(cmd,print)]]\ 1680 [list $ffile]\ 1681 [list -back $glob(async)] 1682 } 1683 ld - 1684 wld - 1685 d { PopError [_ "Can't print directories!!"] } 1686 default CantDoThat 1687 } 1688 } 1689} 1690 1691proc CmdMakeSameDir {} { 1692 global glob 1693 if { $glob(left,pwd) == $glob(right,pwd) } { 1694 SandDsame 1695 return {} 1696 } 1697 CmdMakeSameDir_ $glob(listbox,left).file glob(left,filelist) $glob(left,pwd) $glob(right,pwd) 1698 CmdMakeSameDir_ $glob(listbox,right).file glob(right,filelist) $glob(right,pwd) $glob(left,pwd) 1699 UpdateWindow both 1700} 1701 1702proc CmdMakeDir {} { 1703 global config glob 1704 set inst {} 1705 if {[$glob(listbox,left).file curselection] != {}} { 1706 set inst left 1707 } elseif {[$glob(listbox,right).file curselection] != {}} { 1708 set inst right 1709 } 1710 if {[info exists glob(whichdir)]} { 1711 if {$inst == {} ||\ 1712 [$glob(win,$glob(whichdir)).entry_dir get] != \ 1713 [dirToDN $glob($glob(whichdir),pwd)]} { 1714 set inst $glob(whichdir) 1715 } 1716 } 1717 if {$inst == {}} { 1718 PopWarn [_ "\"MkDir\" pushed with nothing selected and display directory \ 1719 unchanged.\n Please do one of these so filerunner\ 1720 knows where the directory is desired."] 1721 return 1722 } 1723 1724 if {[set newdir [$glob(win,$inst).entry_dir get]] == \ 1725 [dirToDN $glob($inst,pwd)]} { 1726 set newdir [simple_smart_dialog "." [_ "Directory name?"] \ 1727 [_ "Please enter the name of the new directory.\ 1728 Another way of creating directories is to enter\ 1729 the name the new directory in one\ 1730 of the directory entries and then pressing the\ 1731 MkDir button"] $newdir] 1732 if {$newdir == ""} return 1733 } 1734 set newdir [DNtoDir $newdir] 1735 Log [_ "Creating directory %s" $newdir] 1736 if {[IsVFS $newdir]} { 1737 Try { VFSmkdir $newdir } "" 1 1738 set glob(forceupdate) 1 1739 } else { 1740 Try { file mkdir $newdir } "" 1 1741 } 1742 UpdateWindow both 1743 set glob(forceupdate) 0 1744} 1745 1746proc CmdSelect {} { 1747 global glob 1748 if { [info exists glob(whichdir)] } { 1749 set inst $glob(whichdir) 1750 set pat [DNtoDir [$glob(win,$inst).entry_dir get]] 1751 } else { 1752 PopInfo [_ "Please enter a selection pattern in one of the\ 1753 directory entries and then press the Select button"] 1754 return 1755 } 1756 setDisplayDir $inst 1757 # $glob(win,$inst).entry_dir delete 0 end 1758 # $glob(win,$inst).entry_dir insert end $glob(${inst},pwd) 1759 set pat [file tail $pat] 1760 set i 0 1761 foreach elem $glob($inst,filelist) { 1762 if {[string match $pat [lindex $elem 1]]} { 1763 $glob(listbox,$inst).file selection set $i 1764 } 1765 incr i 1766 } 1767 propagateSelection $glob(listbox,$inst).file 1768 UpdateStat 1769} 1770 1771proc CmdCSelect {} { 1772 global glob 1773 set cmd [simple_smart_dialog "." [_ "Contents-select"] \ 1774 [_ "Make sure you have selected the files you want to\ 1775 search in, then please edit this command to do a\ 1776 contents-select."] "grep -i "] 1777 if { $cmd == "" } return 1778 CmdCSelect_ $glob(listbox,left).file glob(left,filelist) \ 1779 $glob(left,pwd) $glob(right,pwd) $cmd 1780 CmdCSelect_ $glob(listbox,right).file glob(right,filelist) \ 1781 $glob(right,pwd) $glob(left,pwd) $cmd 1782 UpdateStat 1783} 1784 1785proc CmdCSelect_ { listb_name filelist_var frompwd topwd cmd } { 1786 upvar $filelist_var filelist 1787 1788 foreach sel [$listb_name curselection] { 1789 set elem [lindex $filelist $sel] 1790 switch [lindex $elem 2] { 1791 l - 1792 wl - 1793 n { 1794 set r [catch { 1795 eval exec $cmd { 1796 [DNtoDirTail [URL norm $frompwd/[lindex $elem 1]]] 1797 } 1798 } outp] 1799 if { $r != 0 } { 1800 $listb_name selection clear $sel 1801 } 1802 } 1803 default CantDoThat 1804 } 1805 } 1806} 1807 1808# This searches the run list for 'what' 1809# and returns either -1 (not found) or a list 1810# of indexes to the found entry 1811proc runCmdListSearch {clist what} { 1812 set level -1 1813 foreach en $clist { 1814 incr level 1815 if {[llength $en] != 2} { 1816 if {[lindex $en 0 0] == $what} { 1817 return $level 1818 } 1819 } else { 1820 set rt [runCmdListSearch [lindex $en 1] $what] 1821 if {$rt != -1} { 1822 return [concat $level 1 $rt] 1823 } 1824 } 1825 } 1826 return -1 1827} 1828 1829proc CmdRunCmd {args} { 1830 frputs "Run command called to run $args " 1831 global glob config 1832 set glob(runGlob,run) {} 1833 set thisone {} 1834 # Old call sequence had 0/1 followed by name... 1835 foreach thisone $args { 1836 if {[string is integer $thisone]} { 1837 set thisone {} 1838 continue 1839 } 1840 break 1841 } 1842 if {$thisone != {} } { 1843 # This search is complicated by the tree structure of the runlist. 1844 set indx [runCmdListSearch $config(runlist) $thisone] 1845 if {$indx == -1} { 1846 PopError [_ "Run command: could not find \"%s\" in the runlist."\ 1847 $thisone] 1848 return 1849 } 1850 set glob(runGlob,run) [lindex $config(runlist) {*}$indx] 1851 after idle [list DoProtCmd CmdRunCmd_] 1852 return 1853 } 1854 # none of that, put up a menu and bail... 1855 1856 # This is an convential menu window on which we force a tearoff. 1857 # This allows us to have checkboxes that don't unpost the menu. 1858 # Given that FixTearoff fixes most of the problems with 1859 # tearoff windows, all we are left with is positioning it 1860 # somewhere near the mouse pointer. 1861 1862 # Read Config will eliminate the menu so that we can rebuild 1863 # it with a possibly new runlist. Otherwise, we keep the same 1864 # menu for the duration. 1865 1866 # Unlike other tearoffs we only allow one on the screen at a time. 1867 1868 set w [expr {[info exists glob(runMenu)] ? $glob(runMenu) : {}}] 1869 if {![winfo exists $w]} { 1870 # We need to build a new menu 1871 set w .menu_[incr glob(toplevelidx)] 1872 set glob(runMenu) $w 1873 1874 destroy $w 1875 1876 menu $w\ 1877 -tearoff 1 \ 1878 -title "Run menu"\ 1879 -borderwidth 1 \ 1880 -relief solid \ 1881 -tearoffcommand "FixTearoff GrabTearoff" 1882 1883 $w add checkbutton\ 1884 -label [_ "<Edit entry>"] \ 1885 -variable glob(runEdit) 1886 1887 $w add checkbutton \ 1888 -label [_ "<Delete menu on select>"] \ 1889 -variable glob(runDeleteMenu) 1890 1891 $w add separator 1892 1893 buildRunListMenu 1894 } 1895 # this covers the case where it was but s/he destroyed it 1896 set glob(runDeleteMenu) 1 1897 set glob(runEdit) 0 1898 if {![info exists glob(runMenuTearoff)] || \ 1899 ![winfo exists $glob(runMenuTearoff)]} { 1900 $w invoke 0 1901 } 1902 1903 if {[llength $config(runlist)] == 0} { 1904 set glob(runEdit) 0 1905 wm with $glob(runMenuTearoff) 1906 cmdrunEdit $glob(runGlob,run) 1907 return 1908 } 1909 # Now we have a menu which is in a tearoff frame 1910 # Lets put it near the mouse... 1911 1912 set hm [winfo reqheight $w] 1913 lassign [winfo pointerxy .] msx msy 1914 1915 # If the mouse is in the lower half of the screen, put the bottom of the 1916 # window where the mouse is, otherwise, put the top there 1917 1918 set yloc [expr {$msy < [winfo screenheight .] / 2 ? $msy : $msy - $hm}] 1919 1920 update 1921 # we should have a new tearoff window 1922 # Lets get its true name... 1923 if {[info exists glob(runMenuTearoff)]} { 1924 set wto $glob(runMenuTearoff) 1925 } else { 1926 error "Can not find tearoff window from $w" 1927 } 1928 # wm with $w 1929 wm geo $wto +${msx}+$yloc 1930 wm deicon $wto 1931 1932 frputs "Run menu building " w wm hm ww wh "[wm geo $w] [wm geo $w] " 1933 return 1934} 1935 1936# This code snipit adds the runlist entries to the menu after first 1937# deleting what is there... 1938 1939proc buildRunListMenu {} { 1940 global config glob 1941 set w $glob(runMenu) 1942 $w delete 4 end 1943 1944 buildCasMenu {} \ 1945 [runListMenuBuild $config(runlist)] \ 1946 $w \ 1947 runListButton\ 1948 [list -tearoffcommand FixTearoff ] 1949 1950 $w add command \ 1951 -label [_ "<Add a new program>"] \ 1952 -command "runListButton 1" 1953} 1954 1955 1956 1957# We pass this proc to FixTearoff which will add the two parms, the 1958# origional window and the name of the new tearoff. We need the 1959# ladder to fix a few things... 1960 1961proc GrabTearoff {menu tearoff} { 1962 set ::glob(runMenuTearoff) $tearoff 1963} 1964 1965# The following is driven by glob(runGlob,run) as to what and how 1966# to run the program 1967 1968proc CmdRunCmd_ {} { 1969 global glob config 1970 # default the pwd to left (changes if selection made in right) 1971 set frompwd $glob(left,pwd) 1972 set fl {} 1973 foreach inst {left right} { 1974 foreach sel [$glob(listbox,$inst).file curselection] { 1975 set elem [lindex $glob($inst,filelist) $sel] 1976 switch [lindex $elem 2] { 1977 wl { 1978 lappend fl [TranslateLnk [wLinkName $inst $elem] \ 1979 [lindex $glob($inst,df) 1]] 1980 } 1981 d - 1982 ld - 1983 l - 1984 n { lappend fl [lindex $elem 1] } 1985 default CantDoThat 1986 } 1987 set frompwd $glob($inst,pwd) 1988 } 1989 } 1990 set options [lassign $glob(runGlob,run) namecmd sep] 1991 lassign $namecmd name cmd 1992 1993 if {"last+current" in $options && $glob(select_pry_lr) != {} } { 1994 set secdir $glob($glob(select_pry_lr),pwd) 1995 if { $secdir == $frompwd } { 1996 set secdir "" 1997 } 1998 foreach sel $glob(select_pry_s) { 1999 lappend fl \ 2000 [DNtoDirTail\ 2001 [URL norm ${secdir}/[lindex $glob($glob(select_pry_lr),filelist) $sel 1]] 2002 } 2003 } 2004 # set cmd [FixFormatString $cmd] 2005 # puts "run $cmd sep = $sep" 2006 if {"fullname" in $options} { 2007 set fll $fl 2008 set fl {} 2009 foreach file $fll { 2010 # if we already have a full name, join will just pass the file 2011 lappend fl [file join $frompwd $file] 2012 } 2013 } 2014 # we want to use frECF here. 2015 # This fixes the run string to accept more than one file at the same loc 2016 # and, with the change to -default, takes care the the file seperator issue 2017 # We replace the first spec (one of:%s|%n|%m|%q|%r) with that %b 2018 2019 # First count the specs, keep location of the last one. 2020 set start 0 2021 set count 0 2022 set here [string length $cmd] 2023 while {[set loc [string first "%" $cmd $start]] != -1} { 2024 if {[string index $cmd $loc-1] == {\\}} { 2025 set start [incr loc] 2026 continue 2027 } 2028 set start [incr loc 2] 2029 if {[string index $cmd $loc-1] in {s n q r} } { 2030 incr count 2031 set here $loc 2032 } 2033 } 2034 2035 set ncmd [string range $cmd 0 $here-1] 2036 set spec %[expr {$count > 0 ? [string index $cmd $here-1] : "s"}] 2037 if {[set remains [expr [llength $fl] - $count]] > 0} { 2038 if {$count == 0} { 2039 append ncmd " $spec" 2040 incr remains -1 2041 } 2042 append ncmd [string repeat "${sep}$spec" $remains] 2043 append ncmd [string range $cmd $here end] 2044 set cmd $ncmd 2045 } 2046 set async [expr {"async" in $options}] 2047 if {!$async} { 2048 set op [list -post \ 2049 [list postOptions \ 2050 [list ViewString [_ "Output from %s %s"\ 2051 $name \ 2052 [string index $cmd end]]]\ 2053 nop]] 2054 } else { 2055 set op "-nop" 2056 } 2057 LogSilent [_ "Running command: %s" $cmd] 2058 cd $frompwd 2059 frECF [list exec {*}[subst {*}$::stOps $cmd] {*}[expr {$async ? "&" : {}}]]\ 2060 $fl \ 2061 [list -default "${sep}$spec" -back $async]\ 2062 $op 2063 2064 UpdateWindow both 2065} 2066 2067 2068proc runListMenuBuild {list} { 2069 set runMenu {} 2070 while {[llength $list] > 0} { 2071 set list [lassign $list this] 2072 set more [lassign $this name ent] 2073 if {[string index $name 0] != "-"} { 2074 # simple entry 2075 lappend runMenu [list [lindex $name 0]] 2076 } else { 2077 # a sub list 2078 lappend runMenu [list $name [runListMenuBuild $ent]] 2079 } 2080 } 2081 frputs "Raw run menu " runMenu 2082 return $runMenu 2083} 2084 2085# [list {label} {runstring} {{ }} fullname async] 2086proc cmdrunEdit {what} { 2087 global glob config 2088 set opts [lassign $what namecmd glob(runGlob,file)] 2089 lassign $namecmd glob(runGlob,display) glob(runGlob,run) 2090 set oldDisplayName $glob(runGlob,display) 2091 2092 set glob(runGlob,full) [expr {"fullname" in $opts}] 2093 set glob(async) [expr {"async" in $opts}] 2094 set glob(runGlob,duo) [expr {"last+current" in $opts}] 2095 # default to add to run list 2096 set glob(runGlob,add) [expr {! $glob(runEdit) ? 1 : 0}] 2097 set glob(runNow) 0 2098 2099 set sdr [smart_dialog .run[incr ::uni] \ 2100 . \ 2101 [_ "Run Dialog"] \ 2102 [list [_ "Edit the 'command & ops' line.\ 2103 \n Ctrl-A takes you to the start of the line.\ 2104 \n Tab does command complete.\ 2105 \n Mouse button 3 brings up a browser.\ 2106 \n This command is run through the FileRunner\ 2107 Exec Call Formater (FRECF)\ 2108 \n so all format commands\ 2109 that FRECF supports are available.\ 2110 \n See Help menu->Tips: 'Configure options for commands II'\ 2111 \n A '%s' in this string will be appended with\ 2112 \n sufficient copys for the given file list\ 2113 \n with the requested file seperator between them\ 2114 \n\n'file separator' specifies\ 2115 what string is used to separate files (should there\ 2116 \n be more than one). Default is space ({ }).\ 2117 \n'display name' if supplied will be the name displayed\ 2118 in the run list\ 2119 \n'full file names' will pass the full name rather than\ 2120 the working directory relative name.\ 2121 \n'Last + current' will pass both the last selection\ 2122 and the current selection.\ 2123 \nAn edit operation, unless canceled, will save the\ 2124 result.\ 2125 \n\"Add to run list\" will, in this case, also add the\ 2126 same entry to the end of the list.\ 2127 \n" "%s, %n, %q or %r" ]] \ 2128 0 \ 2129 [expr {$glob(runEdit) ? 11 : 10}] \ 2130 [list [list [_ "command & opts"] \ 2131 "-textvariable glob(runGlob,run) -width 80"] \ 2132 [list [_ "file separator"] \ 2133 "-textvariable glob(runGlob,file)"]\ 2134 [list [_ "display name"] \ 2135 "-textvariable glob(runGlob,display)"] \ 2136 [list [_ "Async"] "-variable glob(async)"]\ 2137 [list [_ "full file\nnames"] \ 2138 "-variable glob(runGlob,full)"]\ 2139 [list [_ "Last +\ncurrent"] \ 2140 "-variable glob(runGlob,duo)"]\ 2141 [list [_ "Add to\nrun list"] \ 2142 "-variable glob(runGlob,add)"]\ 2143 [list [_ "Run \nnow?"] \ 2144 "-variable glob(runNow)"]\ 2145 [list [_ "OK"]] \ 2146 [list [_ "Cancel"]]\ 2147 [list [_ "Delete from\nrun list"]]\ 2148 ]\ 2149 [list -height 16 scroll 15 \ 2150 bind [list .run.0 <Tab> \ 2151 "Complete .run.0\ 2152 .run.text ; break"]\ 2153 bind [list .run.0 <3> \ 2154 "CompleteWithBrowse .run.0"]\ 2155 ]] 2156 if {$sdr == 9 || $sdr == -1 || $glob(runGlob,run) == "" } { 2157 return {} 2158 } 2159 2160 if {$sdr == 10} { 2161 # This is the delete option... 2162 set config(runlist) [ldelete $config(runlist) $glob(runIndex)] 2163 buildRunListMenu 2164 return 2165 } 2166 2167 if {$glob(runGlob,file) == {}} { 2168 set glob(runGlob,file) " " 2169 } 2170 if {$glob(runGlob,display) == {}} { 2171 set glob(runGlob,display) [lindex $glob(runGlob,run) 0] 2172 } 2173 set new {} 2174 if {$glob(runGlob,full)} {lappend new "fullname"} 2175 if {$glob(async)} {lappend new "async"} 2176 if {$glob(runGlob,duo)} {lappend new "last+current"} 2177 set new [list [list $glob(runGlob,display) $glob(runGlob,run)] \ 2178 $glob(runGlob,file) {*}$new] 2179 if {$glob(runEdit)} { 2180 lset config(runlist) {*}$glob(runIndex) $new 2181 } 2182 if {$glob(runGlob,add)} { 2183 # $glob(runMenu) insert [$glob(runMenu) index end] command \ 2184 # -label $glob(runGlob,display)\ 2185 # -command [list runListButton 2 {} [llength $config(runlist)]] 2186 lappend config(runlist) $new 2187 } 2188 if {$glob(runGlob,add) || $glob(runEdit) >= 0} { 2189 buildRunListMenu 2190 SaveConfig 2191 } 2192 set glob(runGlob,run) $new 2193 if {$glob(runNow)} { 2194 DoProtCmd CmdRunCmd_ 2195 } 2196 return 2197} 2198 2199# The run menu sends us here. The parms are: 2200# 2201# val this will be 1 if s/he wants to add to the run list 2202# 2 if loc is the index into the runlist 2203# of the program to run 2204# indx is the display name (we don't use this here) 2205# val is a list of indexies as per 2 above 2206 2207# In aaddition we use glob(runEdit) and glob(runDeleteMenu) 2208 2209proc runListButton {val {indx {0}} {loc {}}} { 2210 global glob config 2211 if {$glob(runDeleteMenu) && [winfo exists $glob(runMenuTearoff)]} { 2212 wm with $glob(runMenuTearoff) 2213 } 2214 if {$val < 1} { 2215 return 2216 } 2217 if {$val == 1} { 2218 set glob(runEdit) 0 2219 if {[cmdrunEdit {}] == {}} { 2220 return 2221 } 2222 } 2223 set glob(runGlob,run) [lindex $config(runlist) {*}$loc] 2224 if {$glob(runEdit) } { 2225 set glob(runIndex) $loc 2226 cmdrunEdit $glob(runGlob,run) 2227 return 2228 } 2229 DoProtCmd CmdRunCmd_ 2230 return 2231} 2232 2233proc CmdForEach {} { 2234 global glob 2235 CmdForEach_ $glob(listbox,left).file glob(left,filelist) \ 2236 $glob(left,pwd) $glob(right,pwd) 2237 CmdForEach_ $glob(listbox,right).file glob(right,filelist) \ 2238 $glob(right,pwd) $glob(left,pwd) 2239} 2240 2241proc CmdForEach_ { listb_name filelist_var frompwd topwd } { 2242 global glob config 2243 upvar $filelist_var filelist 2244 set inst $glob(select_cur_lr) 2245 2246 set fl {} 2247 foreach sel [$listb_name curselection] { 2248 set elem [lindex $filelist $sel] 2249 switch [lindex $elem 2] { 2250 wl { 2251 lappend fl [TranslateLnk [wLinkName $inst $elem] \ 2252 [lindex $glob($inst,df) 1]] 2253 } 2254 d - 2255 ld - 2256 l - 2257 n { lappend fl [lindex $elem 1] } 2258 default CantDoThat 2259 } 2260 } 2261 if { $fl == "" } return 2262 if {$glob(async)} { 2263 PopError [_ "This command does not support asynchronous execution"] 2264 return 2265 } 2266 if {![info exists glob(foreach,cmd)] || $glob(foreach,cmd) == {}} { 2267 set glob(foreach,cmd) {echo '%s'} 2268 } 2269 set glob(foreach,cmd) \ 2270 [simple_smart_dialog "." [_ "Foreach"] \ 2271 [_ "Enter command to run on each of the selected files.\ 2272 The file will show up in the '%s'. A second '%s'\ 2273 (if coded) will be replaced by the opposite panel's\ 2274 working directory. You can use '%s' for the first\ 2275 '%s' to reorder these two. You can use pipes\ 2276 and other bourne-shell syntax elements since the\ 2277 commands will each run in a separate bourne shell\ 2278 in the working directory of the selected file." %s %s %2@s %s]\ 2279 $glob(foreach,cmd) ] 2280 if { $glob(foreach,cmd) == "" } return 2281 2282 set output {} 2283 foreach k $fl { 2284 if {[CheckAbort [_ "ForEach"]]} return 2285 set pcount [expr {[stringCount {%} $glob(foreach,cmd)] - \ 2286 (2 * [stringCount {%%} $glob(foreach,cmd)])}] 2287 set k [list $k] 2288 if {$pcount > 1} { 2289 lappend k $topwd 2290 } 2291 Log [_ "Running %s ..." [list exec [subst {*}$::stOps $glob(foreach,cmd)] $k]] 2292 2293 set rr [frECF [list exec {*}[subst {*}$::stOps $glob(foreach,cmd)]] $k] 2294 2295 2296 append output [lindex $rr 1] 2297 append output "\n" 2298 } 2299 ViewString [_ "Output from commands"] output 2300 UpdateWindow both 2301} 2302 2303proc CmdRecurseCommand { inst } { 2304 global glob config 2305 2306 set dir $glob($inst,pwd) 2307 2308 if { [IsVFS $dir] } { 2309 CantDoThat 2310 return 2311 } 2312 CmdFind $inst ok 2313 return 2314} 2315 2316proc CmdDiff {} { 2317 global glob 2318 CmdDiff_ $glob(listbox,left).file glob(left,filelist) \ 2319 $glob(left,pwd) $glob(right,pwd) left 2320 2321 CmdDiff_ $glob(listbox,right).file glob(right,filelist) \ 2322 $glob(right,pwd) $glob(left,pwd) right 2323} 2324 2325proc CmdDiff_ { listb_name filelist_var frompwd topwd inst } { 2326 global glob 2327 upvar $filelist_var filelist 2328 global config 2329 2330 set null 1 2331 set file1 "" 2332 set file2 "" 2333 foreach sel [$listb_name curselection] { 2334 set null 0 2335 set elem [lindex $filelist $sel] 2336 lassign $elem du ffile swt size 2337 switch -glob $swt { 2338 wl* { 2339 set ffile [TranslateLnk [wLinkName $inst $elem] \ 2340 [lindex $glob($inst,df) 1]] 2341 } 2342 default {set ffile [DNtoDirTail [URL norm $frompwd/$ffile]]} 2343 } 2344 switch $swt { 2345 ld - 2346 fld - 2347 d - 2348 fd - 2349 l - 2350 fl - 2351 fn - 2352 n { 2353 if {$file1 == ""} { 2354 set file1 $ffile 2355 if {[IsVFS $file1]} { 2356 set file1 [MoveToTmp $file1 $swt $size] 2357 } 2358 } else { 2359 if { $file2 != "" } { 2360 PopError [_ "Please select one or two\ 2361 files or directories for diffing."] 2362 return 2363 } 2364 set file2 $ffile 2365 if {[IsVFS $file2]} { 2366 set file2 [MoveToTmp $file2 $swt $size 1] 2367 } 2368 # But which was first? 2369 if {$inst == $glob(select_pry_lr) && 2370 $sel == $glob(select_pry_s)} { } else { 2371 set tmp $file1 2372 set file1 $file2 2373 set file2 $tmp 2374 } 2375 } 2376 } 2377 default { 2378 CantDoThat 2379 return 2380 } 2381 } 2382 } 2383 2384 if {$null} return 2385 2386 if {$file2 == "" && $glob(select_pry_lr) != {}} { 2387 set sel $glob(select_pry_s) 2388 if {[llength $sel] == 1} { 2389 set elem2 [lindex $glob($glob(select_pry_lr),filelist) $sel] 2390 lassign $elem2 du file2t type2 size2 2391 set file2 [DNtoDirTail [URL norm $glob($glob(select_pry_lr),pwd)/$file2t]] 2392 if {[IsVFS $glob($glob(select_pry_lr),pwd)]} { 2393 set file2 [MoveToTmp $file2 $type2 $size2 1] 2394 # CantDoThat 2395 # return 2396 } 2397 switch -glob [lindex $elem 2] { 2398 wl* { 2399 set file2 [TranslateLnk [wLinkName $glob(select_pry_lr) $elem] \ 2400 [lindex $glob($inst,df) 1]] 2401 } 2402 } 2403 } else { 2404 PopError [_ "Please select one or two\ 2405 files or directories for diffing."] 2406 return 2407 } 2408 } 2409 if {! ($file2 == "")} { 2410 set tmp $file1 2411 set file1 $file2 2412 set file2 $tmp 2413 } 2414 frECF [list exec {*}[subst {*}$::stOps $config(cmd,diff)]] [list $file1 $file2] \ 2415 [list -post \ 2416 [list postOptions \ 2417 [list ViewString [_ "Diffing %s and %s %s" \ 2418 $file1 $file2\ 2419 [string index $config(cmd,diff) end]]]\ 2420 [list PopInfo \ 2421 [_ "No difference between\n\n%s\n\nand\n\n%s" $file1 $file2]]]]\ 2422 [list -error {} -background $glob(async)] 2423 2424 2425} 2426 2427proc CmdCreateEmptyFile { inst } { 2428 global glob config 2429 set start_entry $glob($inst,pwd) 2430 set newfile [simple_smart_dialog "." [_ "Create New File"] \ 2431 [_ "Please enter the name of the new file."] \ 2432 $start_entry ] 2433 2434 if {$newfile != ""} { 2435 Log [_ "Creating new file %s" $newfile] 2436 if {[IsVFS $newfile]} { 2437 # This is a bit complicated in that we really do want to 2438 # do a "touch" and not just blindly create a new file. 2439 # Its simple if it does not exist. If it does, well we 2440 # don't want to bring it here and send it back just to 2441 # change its modify time... 2442 # Try a touch command... 2443 set r [catch {VFScommand $VFStok [list touch $VFSpath]} out] 2444 if {$r != 0} { 2445 PopError "VFS command touch failed: $out" 2446 } 2447 } else { 2448 if {$config(cmd,touch) == {}} { 2449 if {![DoWeHavePackage fileutil touch]} { 2450 PopWarn "config(cmd,touch) is 'nil' and tcl 'fileutil' not found. 2451 \nWe quit!" 2452 return 2453 } 2454 Try {::fileutil::touch "$newfile"} "" 1 2455 } else { 2456 Try { exec $config(cmd,touch) "$newfile" } "" 1 2457 } 2458 } 2459 ForceUpdate 2460 } 2461} 2462 2463proc DeleteVFSRecursive {dir} { 2464 set me "DeleteVFSRecursive" 2465 global glob config 2466 set r [IsVFS $dir] 2467 if {[VFSrmdirEmpty $dir]} { 2468 VFSrmdir $dir 2469 return 2470 } 2471 2472 Log [_ "$me: Entering %s" $dir] 2473 # NewPwd $inst $dir 2474 # UpdateWindow $inst 2475 foreach elem [getFileListFor $dir] { 2476 if {[CheckAbort [_ "Delete"]]} {return} 2477 set file [lindex $elem 1] 2478 switch [lindex $elem 2] { 2479 fn - 2480 fld - 2481 fl { 2482 Log [_ "$me Deleting %s" $dir/$file] 2483 VFSdelete [URL norm $dir/$file] 2484 } 2485 fd { 2486 2487 if { $file != "." } { 2488 if { $file != ".." } { 2489 DeleteVFSRecursive [DNtoDirTail [URL norm $dir/$file]] 2490 } 2491 } 2492 } 2493 default { 2494 error [_ "Unexpected file type in $me"] 2495 } 2496 } 2497 } 2498 Log [_ "$me Deleting %s" $dir] 2499 VFSrmdir $dir 2500} 2501 2502proc CopyFromVFSRecursive { fromdir todir resume } { 2503 set me "CopyFromVFSRecursive" 2504 global glob config 2505 if {[CheckAbort [_ $me]]} return 2506 IsVFS $fromdir 2507 frputs fromdir todir 2508 if {[VFSRcopyOk $fromdir] & 1} { 2509 if {[file tail $fromdir] == [file tail $todir] ||\ 2510 ![VFSsupports $fromdir nocopyas]} { 2511 VFSgetFile $fromdir $todir 0 2512 return 2513 } 2514 } 2515 set dir [file tail $fromdir] 2516 Log [_ "$me: Creating %s" $todir/$dir] 2517 file mkdir "$todir" 2518 Log [_ "$me: Entering %s" $fromdir] 2519 # NewPwd $inst $fromdir 2520 # UpdateWindow $inst 2521 foreach elem [getFileListFor $fromdir] { 2522 if {[CheckAbort [_ $me]]} return 2523 lassign $elem duh file type size 2524 set tof $todir/$file 2525 switch $type { 2526 fld - 2527 fl { 2528 Log [_ "$me Skipping %s - link" $fromdir/$file] 2529 } 2530 fd { 2531 if {$file ni {. ..}} { 2532 CopyFromVFSRecursive "$fromdir/$file" \ 2533 "$tof" $resume 2534 } 2535 } 2536 fn { 2537 Log [_ "$me: Copying %s -> %s bytes)" \ 2538 $fromdir/$file \ 2539 "$todir/$dir/$file $size"] 2540 VFSgetFile "$fromdir/$file" \ 2541 "$todir/$file" "$size" $resume 2542 } 2543 default { 2544 error [_ "Unexpected file type in $me"] 2545 } 2546 } 2547 } 2548} 2549 2550proc CopyToVFSRecursive { fromdir todir} { 2551 set me "CopyToVFSRecursive" 2552 global glob config 2553 if {[CheckAbort [_ $me]]} return 2554 frputs fromdir todir 2555 IsVFS $todir 2556 if {[VFSRcopyOk $todir] & 2} { 2557 if {[file tail $fromdir] == [file tail $todir] ||\ 2558 ![VFSsupports $todir nocopyas]} { 2559 # begin exp 2560 if {0} { 2561 frputs fromdir todir 2562 if {[VFSisDir $todir] == {}} { 2563 # we know it should be a dir, but it does not exist... 2564 VFSmkdir $todir 2565 } 2566 VFSputFile [URL dir $todir] $fromdir 0 2567 return 2568 } 2569 2570 VFSputFile $todir $fromdir 0 2571 return 2572 } 2573 } 2574 Log [_ "$me: Creating %s" $todir] 2575 # VFSmkdir "[URL norm $todir/$dir]" 2576 if {[fileType $todir] != "d"} { 2577 VFSmkdir $todir 2578 } 2579 Log [_ "$me: Entering %s" $fromdir] 2580 # NewPwd $inst $fromdir 2581 # UpdateWindow $inst 2582 foreach elem [getFileListFor $fromdir] { 2583 if {[CheckAbort [_ $me]]} return 2584 lassign $elem duh file type size 2585 # set file [lindex $elem 1] 2586 set tof [URL norm $todir/$file] 2587 switch $type { 2588 ld - 2589 l { 2590 Log [_ "$me Skipping %s - link" $fromdir/$file] 2591 } 2592 d { 2593 if {$file ni {. ..}} { 2594 CopyToVFSRecursive "$fromdir/$file"\ 2595 $tof 2596 } 2597 } 2598 n { 2599 Log [_ "$me: Copying %s -> %s %s bytes)"\ 2600 "$fromdir/$file" $tof $size] 2601 VFSputFile $tof "$fromdir/$file" $size 2602 } 2603 default { 2604 error [_ "Unexpected file type in $me"] 2605 } 2606 } 2607 } 2608} 2609 2610# I may be dense, but I have not found a simple way to get 'file copy' 2611# to recursivly copy directories if the destination directory exists. 2612# It seems to do fine if it does not. 2613# Here is an 'file copy' replacement that does the same thing as 'xcopy' 2614# and 'cp'. 2615 2616proc frFileCopy {from to} { 2617 # First the simple... 2618 set tail [file tail $from] 2619 if {$tail in {. ..}} {return} 2620 set nextTo $to/$tail 2621 if {![file exists $nextTo] || ![file isdir $from]} { 2622 file copy -force $from $to 2623 return 2624 } 2625 foreach file [glob -nocomplain -directory $from .* *.*] { 2626 frFileCopy $file $nextTo 2627 } 2628 return 2629} 2630 2631proc FindDialog {inst pat} { 2632 # we use a special version of viewstring where we provide the call 2633 # to the string search... 2634 #set result [join [lsort $result] "\n"] 2635 set result "" 2636 return [ViewString \ 2637 [_ "Find %s in %s Click on file to display in panel" \ 2638 $pat "[pwd]/*" ] \ 2639 result SearchConfig [list FindConfig $inst]] 2640} 2641 2642proc FindConfig {inst w title filename result} { 2643 global glob 2644 upvar 2 $result string 2645 centerWin $w 2646 textSearch $w.text $title "+buildViewConfig" \ 2647 {} \ 2648 [list \ 2649 {Sort on tail} "findSort $w.text t" \ 2650 {Sort on full name} "findSort $w.text f" \ 2651 {Save As...} "SaveToFile $w.text [list $glob($inst,pwd)/] 1" \ 2652 [_ Quit] "destroy $w"] 2653 bind $w.text <1> "GotoFind \"$glob($inst,pwd)\" \"\[findGetLine $w.text %x %y]\" $inst;break" 2654 #puts "$string" 2655} 2656 2657proc findGetLine {w x y} { 2658 lassign [split [$w index @$x,$y] .] line 2659 return [$w get $line.0 $line.end] 2660} 2661proc findSort {w opt} { 2662 set files [split [$w get 0.0 end] \n] 2663 $w delete 0.0 end 2664 $w mark set insert 0.0 2665 set sort {} 2666 foreach file $files { 2667 if {$file != {} } { 2668 if {$opt == "t"} { 2669 lappend sort [file tail $file]$file 2670 } else { 2671 lappend sort $file 2672 } 2673 } 2674 } 2675 #puts "$sort" 2676 set sindex [lsort -indices $sort] 2677 foreach idx $sindex { 2678 $w insert insert "[lindex $files $idx]\n" 2679 } 2680 # Lets not ask about saving this file... 2681 $w edit reset 2682 $w edit modified 0 2683} 2684 2685proc GotoFind { dir file inst } { 2686 global glob config 2687 frputs "gfind [pwd] " inst dir file 2688 if {[string index $file 0] == " "|| $file == {}} {return 0} 2689 set file [URL norm $dir/$file] 2690 NewPwd $inst [URL dirname $file] 2691 UpdateWindow $inst 2692 set name [file tail $file] 2693 if {$name == {}} {return 1} 2694 # use search here? need ordinal number 2695 if {[set j [lsearch -exact -index 1 $glob($inst,filelist) $name]] != -1} { 2696 $glob(listbox,$inst).file selection clear 0 end 2697 $glob(listbox,$inst).file selection set $j 2698 $glob(listbox,$inst).file see $j 2699 propagateSelection $glob(listbox,$inst).file 2700 return 1 2701 } 2702 # File not found. Could it be hidden? 2703 if {$config(fileshow,all) == 0} { 2704 if {!$::MSW} { 2705 set h [expr {[string index $name 0] == "."}] 2706 } else { 2707 set h [file attributes $file -hidden] 2708 } 2709 if {$h} { 2710 PopError [_ "File %s is a \"hidden\" file which are\ 2711 \nnot displayed if \"Show All File\" configuration is false." $file] 2712 return 1 2713 } 2714 } 2715 PopError [_ "File %s can not be found." $file] 2716} 2717 2718# fileutilFindAsyncHandler is an async function to set up a call to 2719# ::fileutil::find. It will provide a compare function based on the 2720# cmds and will log results to 'logFun' 2721# 2722proc fileutilFindAsyncHandler {w cmds logFun} { 2723 # first remove the >2 stuff 2724 if {[lindex $cmds end-1] == ">2"} {set cmds [lreplace $cmds end-1 end]} 2725 set ::findStop$w 0 2726 set parms [lindex $logFun 1 ] 2727 frputs "fileutilFindAsync " 2728 set r [catch {::realfind::realFind [list [pwd]] \ 2729 "$cmds" [linsert $logFun end "::findStop$w" "data"]} what] 2730 if {$r !=0} { 2731 #puts "rtn $what" 2732 findData $parms $w d data " ${what}\n" 2733 findData $parms $w d data " $::errorInfo" 2734 } else { 2735 #puts "rtn 0 $what" 2736 foreach line $what { 2737 findData $parms $w d data " $line" 2738 } 2739 } 2740 findData $parms $w d eof 2741 unset ::findStop$w 2742 2743} 2744 2745proc CmdFind { inst {ext -1}} { 2746 global glob config 2747 set vfs [IsVFS $glob($inst,pwd)] 2748 catch "destroy .apop" 2749 if {![info exists glob(searchstring)]} { 2750 set glob(find,string) "" 2751 set glob(find,options) {} 2752 set glob(find,expressions) {} 2753 set glob(find,regexp) 0 2754 set glob(find,casefold) 0 2755 } 2756 if {$ext != -1 && ![info exists glob(foreach,all)]} { 2757 set glob(foreach,all) 0 2758 set glob(foreach,cmd) {} 2759 } 2760 set smart_buts \ 2761 [list \ 2762 [list [_ "name or regex pattern"] {-textvariable glob(find,string)}]\ 2763 [list [_ "extra options (-L,-P,-D,-O)"] {-textvariable glob(find,options)}]\ 2764 [list [_ "extra expressions and tests"] {-textvariable glob(find,expressions)}]\ 2765 [list [_ "regexp"] {-variable glob(find,regexp)}]\ 2766 [list [_ "casefold"] {-variable glob(find,casefold)}]\ 2767 [list {} [list -text [_ "\nEnter <command> to run on each file\ 2768 or all files as a group\ 2769 (option <all files>). The file(s) will show up in the '%s'\ 2770 (or at the end of the command if %s is not coded).\ 2771 \nThe commands are run directly by the tcl exec command.\ 2772 You can use pipes and other tcl exec constructs.\ 2773 You can also enter a shell as the first part of the\ 2774 command to pickup shell syntax elements." \ 2775 "%s" "%s" ] \ 2776 -wraplength [expr {60 * [font measure $config(gui,ListBoxFont) {0}]}]\ 2777 -justify left]\ 2778 ]\ 2779 [list {} {-textvariable glob(foreach,cmd)}]\ 2780 [list [_ "all files"] {-variable glob(foreach,all)}]\ 2781 [_ OK] [_ Cancel]] 2782 2783 if {$vfs} { 2784 set smart_buts [lreplace $smart_buts 1 4] 2785 } 2786 if {$ext == -1} { 2787 set title [_ "Find..."] 2788 set startMes [list {} [_ "Please enter substring of filename\ 2789 to search for in %s and below." $glob($inst,pwd)]] 2790 set smart_buts [lreplace $smart_buts end-4 end-2] 2791 } else { 2792 set title [_ "Run Command Recursively"] 2793 set startMes [list [_ "This command will run:\ 2794 \n\n find %s <options>\ 2795 \n\n(with <options> selected below) to\ 2796 recurse into the current directory (%s).\ 2797 \nThe below entered <command> will then be run on all files\ 2798 from this find (either singly or as a group).\n\ 2799 \nSee also: manpage for the find command.\ 2800 \n\nUse tab to go to next entry.\ 2801 Return or the OK button starts execution."\ 2802 $glob($inst,pwd) $glob($inst,pwd)]] 2803 } 2804 set butCount [llength $smart_buts] 2805 2806 set r [smart_dialog .dbl_entry_dialog[incr ::uni] "." [_ "Find..."]\ 2807 $startMes \ 2808 -1 $butCount\ 2809 $smart_buts [buildDialogConfig] \ 2810 [list bind [list .dbl_entry_dialog.1 <Return> \ 2811 "event generate .dbl_entry_dialog.0 <Tab>"]]] 2812 2813 if {$r == -1 || $r == ($butCount - 1)} {return} 2814 2815 #----------------------------------- vfs code ------------------------- 2816 if {$vfs} { 2817 if {$glob(find,string) == {}} {return} 2818 set r [catch {VFScd $glob($inst,pwd)} out] 2819 if {$r != 0} { 2820 PopError $out 2821 return 2822 } 2823 LogStatusOnly [_ "Searching, please wait..."] 2824 set r [catch {VFSsearch $VFStok $glob(find,string)} out] 2825 LogStatusOnly [_ "Searching, please wait...done"] 2826 if {$r} { 2827 PopError [_ "VFS search error: %s" $out] 2828 return 2829 } 2830 ViewString [_ "VFS search results"] out 2831 return 2832 } 2833 #----------------------------------------end of vfs code ------------------ 2834 set r [catch {cd $glob($inst,pwd)} out] 2835 if {$r} { 2836 PopError "$out" 2837 return 2838 } 2839 if {$ext != -1} { 2840 set ext [list $glob(foreach,all) $glob(foreach,cmd)] 2841 } 2842 if {$glob(find,expressions) == {} && $glob(find,string) == {}} {return} 2843 set pcall {} 2844 if {$glob(find,string) != {}} { 2845 # build the proper pattern call out name, iname regex iregex 2846 switch -exact $glob(find,regexp)$glob(find,casefold) { 2847 00 {set pcall "-name"} 2848 01 {set pcall "-iname"} 2849 10 {set pcall "-regex"} 2850 11 {set pcall "-iregex"} 2851 } 2852 } 2853 set pat {} 2854 if {$glob(find,string) != {}} { 2855 if {$glob(find,regexp)} { 2856 set pat .*$glob(find,string).* 2857 } else { 2858 set pat *$glob(find,string)* 2859 } 2860 } 2861 # Use the frECF to get this right... 2862 set cmds [slList {*}[subst {*}$::stOps $config(cmd,find)] \ 2863 {*}$glob(find,options) [pwd]/ $pcall %n \ 2864 {*}$glob(find,expressions) -print 2> \ 2865 [expr {$::MSW ? "nul" : "/dev/null"}]] 2866 set cmds [frECF $cmds [list $pat] -f 1] 2867 frputs "parts of find " cmds glob(find,string) pat 2868 set w [FindDialog $inst "$pat $glob(find,expressions)"] 2869 set logFun [list findlog [list findData [list [list $ext [list [pwd]]]] $w]] 2870 if {$config(cmd,find) == {}} { 2871 set cmds [slList {*}$glob(find,options) $pcall \ 2872 $pat {*}$glob(find,expressions)] 2873 2874 set geo [buildStop $w \ 2875 [_ "Searching, %s for %s. Please wait..." "[pwd]/*" "$pat" ] \ 2876 "set ::findStop$w" 1] 2877 Try {fileutilFindAsyncHandler $w "$cmds" \ 2878 {findData { $ext [list [pwd]]} $w}} -a 2879 } else { 2880 # We have a race here as 'buildStop' needs to do an update which 2881 # means that calling after we call pipeoExec allows the pipo command 2882 # to send us stuff before we are ready. So, 2883 # Here we set a dummy call back to 'update' which should never be 2884 # called and then after the pipeo call we update the call back 2885 # function to what it ought to be (for which we need the fid that 2886 # pipeo returns. 2887 set geo [buildStop $w \ 2888 [_ "Searching, %s for %s. Please wait..." "[pwd]/*" "$pat" ] \ 2889 update 1] 2890 lassign [pipeoExec $cmds r $logFun] r fid 2891 if {$r != 0} { 2892 destroy $w 2893 error [_ "Bad find call: %s \n %s" $fid $cmds] 2894 } 2895 stopReSetCallBack $w "pipeoAbort $fid" 2896 } 2897 if {[winfo exists $w]} {wm geo $w $geo} 2898} 2899 2900proc findData {parms w fid tag {arg {}}} { 2901 global config glob 2902 lassign $parms ext pwd 2903 frputs "[incr ::xx] " parms ext pwd fid tag arg 2904 set loc [$w.text index end-1chars] 2905 lassign [split $loc .] line char 2906 if {$tag == "data" } { 2907 if {$ext == "com"} { 2908 $w.text insert insert ${arg} 2909 } else { 2910 if {[string index $arg end] != "\n" } { 2911 StopProgress $w $arg 2912 } else { 2913 if {[string compare -length [string length $pwd] $pwd ${arg}] == 0} { 2914 $w.text insert insert [string range ${arg} [string length $pwd]+1 end] 2915 } else { 2916 $w.text insert insert ${arg} 2917 } 2918 } 2919 # This might be excessive, but, at least on my box, it works well. 2920 intelWinSize $config(geometry,textviewer) $w.text increase 1 location bound 2921 2922 $w.text see insert 2923 } 2924 return 2925 } 2926 # must be end of file. If no data, kill the window and use a popup... 2927 LogStatusOnly [_ "...done"] 2928 set loc [$w.text index end-1chars] 2929 if {[$w.text index insert] == 1.0} { 2930 destroy $w 2931 if {$ext == "com"} { 2932 PopInfo [_ "No output"] 2933 } else { 2934 PopInfo [_ "No files found"] 2935 } 2936 return 2937 } 2938 # At this point we have a list of found files 2939 # If ext is -1 we just remove the stop button and 2940 # sort the files 2941 # Otherwise, ext will be 1 if we are to send all files 2942 # to the command and 0 if we are to send them one at a time 2943 # In either case we use pipeo an give a running result 2944 # But first lets set up a more appropiate window size 2945 2946 # This location for the following two lines is much more relaxed, but on long searches 2947 # it scrunches up the window. 2948 2949 # lassign [intelWinSize "75x5" $config(geometry,textviewer) [$w.text get 0.0 end]] width height 2950 # wm geo $w [getGeo g${width}x$height $w -win $w.text] 2951 frputs "Phase 2? " ext 2952 if {$ext == -1} { 2953 findSort $w.text f 2954 } else { 2955 if {[lindex $ext 0] == "com"} { 2956 incr ::[set w]_flag 2957 return 2958 } 2959 lassign $ext all cmd 2960 # if {[string first {%s} $cmd] == -1} { 2961 # append cmd " %s" 2962 # } 2963 # get the file, remove any error lines... 2964 set files {} 2965 foreach fl [split [$w.text get 0.0 end] \n] { 2966 if {$fl == {} || [string index $fl 0] == " "} {continue} 2967 lappend files $fl 2968 } 2969 frputs "phase 2 " files 2970 $w.text delete 0.0 end 2971 set Log [list findlog [list findData [list [list com [pwd]]] $w]] 2972 # foreach fl $files { 2973 # lappend fll [FixFileNameO [file native $fl] 3 { }] 2974 # } 2975 foreach fl $files { 2976 if {$all == 1} { 2977 set fl $files 2978 # set fl [join $fll " "] 2979 } else { 2980 set fl [list $fl] 2981 } 2982 # set realCmd "[ReSpaceString {} [format $cmd [braceToQuote $fl]]]\n" 2983 set realCmd [frECF [list {*}[subst {*}$::stOps $cmd]] $fl -f 1] 2984 frputs realCmd 2985 # set realCmd [subst -noc -nov $realCmd] 2986 StopProgress $w $realCmd 2987 frputs realCmd fl 2988 lassign [pipeoExec $realCmd r $Log] r fid 2989 if {$r != 0} { 2990 destroy $w 2991 error [_ "Bad find call: %s \n %s" $fid $cmd] 2992 } 2993 stopReSetCallBack $w "pipeoAbort $fid" 2994 # wait for completion... 2995 vwait ::[set w]_flag 2996 if {$all == 1} {break} 2997 } 2998 catch "unset ::[set w]_flag" 2999 } 3000 StopButRemove $w 3001 3002} 3003 3004proc findlog {fid tag} {Log "Find log: $tag"} 3005 3006proc CmdChmod {} { 3007 global glob 3008 CmdChmod_ $glob(listbox,left).file glob(left,filelist) \ 3009 $glob(left,pwd) $glob(right,pwd) left 3010 CmdChmod_ $glob(listbox,right).file glob(right,filelist) \ 3011 $glob(right,pwd) $glob(left,pwd) right 3012} 3013 3014proc CmdChmod_ { listb_name filelist_var frompwd topwd inst } { 3015 # For speed, (hopefully) we'll call by reference... 3016 global config glob 3017 upvar $filelist_var filelist 3018 set fl {} 3019 3020 3021 foreach sel [$listb_name curselection] { 3022 if {[CheckAbort [_ "Chmod"]]} return 3023 set elem [lindex $filelist $sel] 3024 lassign $elem {*}$glob(fListEl) 3025 set file [DNtoDirTail [URL norm $frompwd/$file]] 3026 set localMode [file attributes $file -permissions] 3027 lassign [split $usergroup /] user group 3028 switch $type { 3029 l - 3030 ld - 3031 n - 3032 d { 3033 incr ftpFlag 0 3034 if {$::MSW} { 3035 PopInfo [_ "So sorry, can't do chmod on windows files."] 3036 return 3037 } 3038 } 3039 default { 3040 # Must be an nVFS, maybe we can do it... up to the handler and remote 3041 incr ftpFlag 3042 } 3043 } 3044 lappend fl $file 3045 } 3046 # Note to me, we present the flags to the last file in the list 3047 # but apply the result to all files... 3048 if {$fl != ""} { 3049 set rarg [ChmodDialog $file $localMode $user $group] 3050 lassign $rarg arg ownarg 3051 if {$arg != ""} { 3052 if { $ftpFlag } { 3053 # for ftp we will use a seperate call for each file... 3054 foreach file $fl { 3055 set r [catch {VFSchmod $fl $arg} m ] 3056 if { $r != 0 && $r != "" } { 3057 PopError [_ "VFS chmod returns: %s" $m] 3058 break 3059 } 3060 } 3061 } else { 3062 # we need to do this to get the quotes right... 3063 frECF [list exec {*}[subst {*}$::stOps $config(cmd,chmod)] {*}$arg]\ 3064 $fl\ 3065 [list -back $glob(async)] 3066 } 3067 ForceUpdate $inst 3068 } 3069 if {$ownarg != ""} { 3070 if { $ftpFlag } { 3071 # for ftp we will use a seperate call for each file... 3072 foreach file $fl { 3073 set r [catch {VFSchown $fl $ownarg} m] 3074 if { $r != "0" && $r != "" } { 3075 PopError [_ "VFS chown returns: %s" $m] 3076 break 3077 } 3078 } 3079 } else { 3080 frECF [list exec {*}[subst {*}$::stOps $config(cmd,chown)] {*}$ownarg]\ 3081 $fl\ 3082 [list -back $glob(async)] 3083 3084 } 3085 ForceUpdate $inst 3086 } 3087 3088 } 3089} 3090 3091proc CmdGetHttp { inst } { 3092 global glob config 3093 3094 if {[IsVFS $glob($inst,pwd)]} { 3095 PopInfo [_ "You can only download HTTP files to a non-VFS directory"] 3096 return 3097 } 3098 3099 if {![info exists glob(http,lasturl)]} { 3100 set glob(http,lasturl) {} 3101 } 3102 set URL $glob(http,lasturl) 3103 3104 while { 1 } { 3105 set URL [simple_smart_dialog "." [_ "Get HTTP File"] \ 3106 [_ "Please enter HTTP URL to download"] $URL ] 3107 if {$URL == ""} { 3108 return 3109 } 3110 3111 if {[string range $URL 0 6] != "http://" } { 3112 set URL "http://$URL" 3113 } 3114 set glob(http,lasturl) $URL 3115 3116 if {![IsVFS $URL] || $VFSpro ni {http https}} { 3117 PopError [_ "Could not parse %s as an HTTP(s) URL" $URL] 3118 continue 3119 } 3120 if {$VFSadd == {}} { 3121 append URL / 3122 } 3123 break 3124 } 3125 3126 set filename [file tail $URL] 3127 if {[string range $URL end end] == "/" && [file ext $filename] == {}} { 3128 append filename .html 3129 } 3130 set filename [simple_smart_dialog "." [_ "Get HTTP File"] \ 3131 [_ "Please edit filename to save to.\n(URL: %s)" $URL] \ 3132 $filename] 3133 if {$filename == ""} { 3134 return 3135 } 3136 cd $glob($inst,pwd) 3137 HTTP_Get $URL $filename 3138 UpdateWindow both 3139} 3140 3141