|
|
|
@ -114,6 +114,9 @@ if {"windows" eq $::tcl_platform(platform)} {
|
|
|
|
|
tcl::namespace::eval punk::nav::fs { |
|
|
|
|
tcl::namespace::export {[a-z]*} ;# Convention: export all lowercase |
|
|
|
|
|
|
|
|
|
#Both tcl's notion of pwd and VIRTUAL_CWD can be out of sync with the process CWD. This happens when in a VFS. |
|
|
|
|
#We can also have VIRTUAL_CWD navigate to spaces that Tcl's cd can't - review |
|
|
|
|
|
|
|
|
|
variable VIRTUAL_CWD ;#cwd that tracks pwd except when in zipfs locations that are not at or below a mountpoint |
|
|
|
|
if {![interp issafe]} { |
|
|
|
|
set VIRTUAL_CWD [pwd] |
|
|
|
@ -128,8 +131,15 @@ tcl::namespace::eval punk::nav::fs {
|
|
|
|
|
} |
|
|
|
|
return $::punk::nav::fs::VIRTUAL_CWD |
|
|
|
|
} |
|
|
|
|
#Both tcl's notion of pwd and VIRTUAL_CWD can be out of sync with the process CWD. This happens when in a VFS. |
|
|
|
|
|
|
|
|
|
#TODO - maintain per 'volume/server' CWD |
|
|
|
|
#e.g cd and ./ to: |
|
|
|
|
# d: |
|
|
|
|
# //zipfs: |
|
|
|
|
# //server |
|
|
|
|
# https://example.com |
|
|
|
|
# should return to the last CWD for that volume/server |
|
|
|
|
|
|
|
|
|
#VIRTUAL_CWD follows pwd when changed via cd |
|
|
|
|
set stackrecord [commandstack::rename_command -renamer punk::nav::fs cd {args} { |
|
|
|
|
if {![catch { |
|
|
|
@ -238,34 +248,44 @@ tcl::namespace::eval punk::nav::fs {
|
|
|
|
|
set atail [lassign $args a1] |
|
|
|
|
if {[llength $args] == 1} { |
|
|
|
|
set a1 [lindex $args 0] |
|
|
|
|
if {$a1 in [list . .. "./" "../"]} { |
|
|
|
|
if {$a1 in [list ".." "../"]} { |
|
|
|
|
switch -exact -- $a1 { |
|
|
|
|
. - ./ { |
|
|
|
|
tailcall punk::nav::fs::d/ |
|
|
|
|
} |
|
|
|
|
.. - ../ { |
|
|
|
|
if {$VIRTUAL_CWD eq "//zipfs:/" && ![string match //zipfs:/* [pwd]]} { |
|
|
|
|
#exit back to last nonzipfs path that was in use |
|
|
|
|
set VIRTUAL_CWD [pwd] |
|
|
|
|
} else { |
|
|
|
|
#set up1 [file normalize [file join $VIRTUAL_CWD ..]] ;#doesn't work! file normalize bug? review - submit ticket? |
|
|
|
|
set up1 [file dirname $VIRTUAL_CWD] |
|
|
|
|
if {[string match //zipfs:/* $up1]} { |
|
|
|
|
if {[Zipfs_path_within_zipfs_mounts $up1]} { |
|
|
|
|
cd $up1 |
|
|
|
|
set VIRTUAL_CWD $up1 |
|
|
|
|
} else { |
|
|
|
|
set VIRTUAL_CWD $up1 |
|
|
|
|
} |
|
|
|
|
tailcall punk::nav::fs::d/ |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#we need to use normjoin to allow navigation to //server instead of just to //server/share (//server browsing unimplemented - review) |
|
|
|
|
# [file join //server ..] would become /server/.. - use normjoin to get //server |
|
|
|
|
# file dirname //server/share would stay as //server/share |
|
|
|
|
#set up1 [file dirname $VIRTUAL_CWD] |
|
|
|
|
set up1 [punk::path::normjoin $VIRTUAL_CWD ..] |
|
|
|
|
if {[string match //zipfs:/* $up1]} { |
|
|
|
|
if {[Zipfs_path_within_zipfs_mounts $up1]} { |
|
|
|
|
cd $up1 |
|
|
|
|
set VIRTUAL_CWD $up1 |
|
|
|
|
} else { |
|
|
|
|
cd $a1 |
|
|
|
|
#set VIRTUAL_CWD [file normalize $a1] |
|
|
|
|
set VIRTUAL_CWD $up1 |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
cd $up1 |
|
|
|
|
#set VIRTUAL_CWD [file normalize $a1] |
|
|
|
|
} |
|
|
|
|
tailcall punk::nav::fs::d/ |
|
|
|
|
} |
|
|
|
|
tailcall punk::nav::fs::d/ |
|
|
|
|
} |
|
|
|
|
if {[file pathtype $a1] ne "relative" && ![string match //zipfs:/* $a1]} { |
|
|
|
|
if {[file type $a1] eq "directory"} { |
|
|
|
|
cd $a1 |
|
|
|
|
#set VIRTUAL_CWD $a1 |
|
|
|
|
tailcall punk::nav::fs::d/ |
|
|
|
|
|
|
|
|
|
if {[file pathtype $a1] ne "relative"} { |
|
|
|
|
if { ![string match //zipfs:/* $a1]} { |
|
|
|
|
if {[file type $a1] eq "directory"} { |
|
|
|
|
cd $a1 |
|
|
|
|
#set VIRTUAL_CWD $a1 |
|
|
|
|
tailcall punk::nav::fs::d/ |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -279,6 +299,7 @@ tcl::namespace::eval punk::nav::fs {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if {![regexp {[*?]} $a1]} { |
|
|
|
|
#NON-Glob target |
|
|
|
|
#review |
|
|
|
|
if {[string match //zipfs:/* $a1]} { |
|
|
|
|
if {[Zipfs_path_within_zipfs_mounts $a1]} { |
|
|
|
@ -287,12 +308,15 @@ tcl::namespace::eval punk::nav::fs {
|
|
|
|
|
set VIRTUAL_CWD $a1 |
|
|
|
|
set curdir $a1 |
|
|
|
|
} else { |
|
|
|
|
set target [punk::path::normjoin $VIRTUAL_CWD $a1] |
|
|
|
|
if {[string match //zipfs:/* $VIRTUAL_CWD]} { |
|
|
|
|
if {[Zipfs_path_within_zipfs_mounts $VIRTUAL_CWD/$a1]} { |
|
|
|
|
commandstack::basecall cd $VIRTUAL_CWD/$a1 |
|
|
|
|
if {[Zipfs_path_within_zipfs_mounts $target]} { |
|
|
|
|
commandstack::basecall cd $target |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
set VIRTUAL_CWD $VIRTUAL_CWD/$a1 |
|
|
|
|
if {[file type $target] eq "directory"} { |
|
|
|
|
set VIRTUAL_CWD $target |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
tailcall punk::nav::fs::d/ |
|
|
|
|
} |
|
|
|
@ -721,7 +745,7 @@ tcl::namespace::eval punk::nav::fs {
|
|
|
|
|
set searchspec [lindex $searchspecs 0] |
|
|
|
|
# -- --- --- --- --- --- --- |
|
|
|
|
set opt_searchbase [dict get $opts -searchbase] |
|
|
|
|
set opt_glob [dict get $opts -tailglob] |
|
|
|
|
set opt_tailglob [dict get $opts -tailglob] |
|
|
|
|
set opt_with_sizes [dict get $opts -with_sizes] |
|
|
|
|
set opt_with_times [dict get $opts -with_times] |
|
|
|
|
# -- --- --- --- --- --- --- |
|
|
|
@ -729,47 +753,72 @@ tcl::namespace::eval punk::nav::fs {
|
|
|
|
|
#we don't want to normalize.. |
|
|
|
|
#for example if the user supplies ../ we want to see ../result |
|
|
|
|
|
|
|
|
|
set relativepath [expr {[file pathtype $searchspec] eq "relative"}] |
|
|
|
|
set searchbase $opt_searchbase |
|
|
|
|
switch -- $opt_glob { |
|
|
|
|
set is_relativesearchspec [expr {[file pathtype $searchspec] eq "relative"}] |
|
|
|
|
if {$opt_searchbase eq ""} { |
|
|
|
|
set searchbase . |
|
|
|
|
} else { |
|
|
|
|
set searchbase $opt_searchbase |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
switch -- $opt_tailglob { |
|
|
|
|
"" { |
|
|
|
|
if {$relativepath} { |
|
|
|
|
set location [file dirname [file join $searchbase $searchspec]] |
|
|
|
|
if {$searchspec eq ""} { |
|
|
|
|
set location |
|
|
|
|
} else { |
|
|
|
|
set location [file dirname $searchspec] |
|
|
|
|
if {$is_relativesarchspec} { |
|
|
|
|
#set location [file dirname [file join $opt_searchbase $searchspec]] |
|
|
|
|
set location [punk::path::normjoin $searchbase $searchspec ..] |
|
|
|
|
} else { |
|
|
|
|
set location [punk::path::normjoin $searchspec ..] |
|
|
|
|
} |
|
|
|
|
#here tail is treated as a search-pattern within location whether or not it contains glob chars "?" or "*" |
|
|
|
|
set match_contents [file tail $searchspec] |
|
|
|
|
} |
|
|
|
|
#here tail is treated as a search-pattern within location whether or not it contains glob chars "?" or "*" |
|
|
|
|
set glob [file tail $searchspec] |
|
|
|
|
} |
|
|
|
|
"\uFFFF" { |
|
|
|
|
set tail_has_globs [regexp {[*?]} [file tail $searchspec]] |
|
|
|
|
if {$tail_has_globs} { |
|
|
|
|
if {$relativepath} { |
|
|
|
|
set location [file dirname [file join $searchbase $searchspec]] |
|
|
|
|
set searchtail_has_globs [regexp {[*?]} [file tail $searchspec]] |
|
|
|
|
if {$searchtail_has_globs} { |
|
|
|
|
if {$is_relativesearchspec} { |
|
|
|
|
#set location [file dirname [file join $searchbase $searchspec]] |
|
|
|
|
#e.g subdir/* or sub/etc/x* |
|
|
|
|
set location [punk::path::normjoin $searchbase $searchspec ..] |
|
|
|
|
} else { |
|
|
|
|
set location [file dirname $searchspec] |
|
|
|
|
set location [punk::path::normjoin $searchspec ..] |
|
|
|
|
} |
|
|
|
|
set glob [file tail $searchspec] |
|
|
|
|
set match_contents [file tail $searchspec] |
|
|
|
|
} else { |
|
|
|
|
#user didn't supply a glob within tail segment, nor did they specify a separate -tailglob - presume they want a directory listing |
|
|
|
|
if {$relativepath} { |
|
|
|
|
set location [file join $searchbase $searchspec] |
|
|
|
|
if {$searchspec eq ""} { |
|
|
|
|
set location $searchbase |
|
|
|
|
} else { |
|
|
|
|
set location $searchspec |
|
|
|
|
if {$is_relativesearchspec} { |
|
|
|
|
#set location [file join $searchbase $searchspec] |
|
|
|
|
set location [punk::path::normjoin $searchbase $searchspec] |
|
|
|
|
} else { |
|
|
|
|
#absolute path for search |
|
|
|
|
set location $searchspec |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
set glob * |
|
|
|
|
set match_contents * |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
default { |
|
|
|
|
#-tailglob supplied separately - ignore any globiness in tail segment of searchspec and treat literally |
|
|
|
|
if {$relativepath} { |
|
|
|
|
set location [file join $searchbase $searchspec] |
|
|
|
|
if {$searchspec eq ""} { |
|
|
|
|
set location $searchbase |
|
|
|
|
} else { |
|
|
|
|
set location $searchspec |
|
|
|
|
if {$is_relativesearchspec} { |
|
|
|
|
#set location [file join $searchbase $searchspec] |
|
|
|
|
set location [punk::path::normjoin $searchbase $searchspec] |
|
|
|
|
} else { |
|
|
|
|
set location $searchspec |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
set glob $opt_glob |
|
|
|
|
set match_contents $opt_tailglob |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
puts stdout "searchbase: $searchbase searchspec:$searchspec" |
|
|
|
|
|
|
|
|
|
set in_vfs 0 |
|
|
|
|
if {[llength [package provide vfs]]} { |
|
|
|
@ -794,7 +843,7 @@ tcl::namespace::eval punk::nav::fs {
|
|
|
|
|
set next_opt_with_times [list -with_times $opt_with_times] |
|
|
|
|
} |
|
|
|
|
if {$in_vfs} { |
|
|
|
|
set listing [punk::du::lib::du_dirlisting_tclvfs $location -glob $glob {*}$next_opt_with_sizes {*}$next_opt_with_times] |
|
|
|
|
set listing [punk::du::lib::du_dirlisting_tclvfs $location -glob $match_contents {*}$next_opt_with_sizes {*}$next_opt_with_times] |
|
|
|
|
} else { |
|
|
|
|
set in_zipfs 0 |
|
|
|
|
if {[info commands ::tcl::zipfs::mount] ne ""} { |
|
|
|
@ -810,9 +859,9 @@ tcl::namespace::eval punk::nav::fs {
|
|
|
|
|
} |
|
|
|
|
if {$in_zipfs} { |
|
|
|
|
#relative vs absolute? review - cwd valid for //zipfs:/ ?? |
|
|
|
|
set listing [punk::du::lib::du_dirlisting_zipfs $location -glob $glob {*}$next_opt_with_sizes {*}$next_opt_with_times] |
|
|
|
|
set listing [punk::du::lib::du_dirlisting_zipfs $location -glob $match_contents {*}$next_opt_with_sizes {*}$next_opt_with_times] |
|
|
|
|
} else { |
|
|
|
|
set listing [punk::du::dirlisting $location -glob $glob {*}$next_opt_with_sizes {*}$next_opt_with_times] |
|
|
|
|
set listing [punk::du::dirlisting $location -glob $match_contents {*}$next_opt_with_sizes {*}$next_opt_with_times] |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -904,7 +953,7 @@ tcl::namespace::eval punk::nav::fs {
|
|
|
|
|
lappend nonportable $nm |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
set front_of_dict [dict create location $location searchbase $searchbase] |
|
|
|
|
set front_of_dict [dict create location $location searchbase $opt_searchbase] |
|
|
|
|
set listing [dict merge $front_of_dict $listing] |
|
|
|
|
|
|
|
|
|
set updated [dict create dirs $dirs files $files filesizes $filesizes nonportable $nonportable flaggedhidden $flaggedhidden underlayfiles $underlayfiles underlayfilesizes $underlayfilesizes] |
|
|
|
@ -1128,6 +1177,8 @@ tcl::namespace::eval punk::nav::fs {
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#REVIEW - at least one version of Tcl during development couldn't navigate using cd to intermediate paths between the zipfs root and the mountpoint. |
|
|
|
|
#TODO - test if this can still occur. |
|
|
|
|
proc Zipfs_path_within_zipfs_mounts {zipfspath} { |
|
|
|
|
if {![string match //zipfs:/* $zipfspath]} {error "Zipfs_path_within_zipfs_mounts error. Supplied zipfspath $zipfspath must be a //zipfs:/* path"} |
|
|
|
|
set is_within_mount 0 |
|
|
|
|