minor updates
authorVince Darley <vincentdarley@sourceforge.net>
Sun, 10 Mar 2002 22:13:17 +0000 (22:13 +0000)
committerVince Darley <vincentdarley@sourceforge.net>
Sun, 10 Mar 2002 22:13:17 +0000 (22:13 +0000)
ChangeLog
Readme.txt
library/mk4vfs.tcl
library/tclprocvfs.tcl
library/vfs.tcl
library/zipvfs.tcl
win/makefile.vc

index 5a1c2f80b1430757b467376c59c89a115985b7e6..046176cd7169ecb4384ee159ab37e120c62aa292 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2002-03-09  Vince Darley <vincentdarley@sourceforge.net>
+       * library/mk4vfs.tcl: updated version from Jean-Claude Wippler.
+
+2002-03-01  Vince Darley <vincentdarley@sourceforge.net>
+       * library/*.tcl: completed and tested most changes from 02-19.
+
 2002-02-19  Vince Darley <vincentdarley@sourceforge.net>
        * library/*.tcl: updated the vfs implementations to deal
        with the 2002-02-01 change below.  More work needed.
index 53347ac5d8ff541d0f34191780667892d4cb340c..009bc9b9b0de550fa2185c8492e6ac297162d581 100644 (file)
@@ -13,9 +13,9 @@ is to expose Tcl 8.4's new filesystem C API to the Tcl level.
 Since 8.4 is still in alpha, the APIs on which this extension depends may of
 course change.  If that happens, it will of course require changes to this
 extension, until the point at which 8.4 goes final, when only
-backwards-compatible changes should occur.  Currently it requires a version
-of Tcl 8.4a4 or newer from September 7th 2001 or newer (if it compiles
-without warning, you should be fine).
+backwards-compatible changes should occur.  Currently it requires the final
+version of Tcl 8.4a4 or newer (from March 8th 2002) --- if it compiles
+without warning, you should be fine.
 
 Using this extension, the editor Alphatk can actually auto-mount, view and
 edit (but not save, since they're read-only) the contents of .zip files
index 32a18e923e7c8017e3f053387b4ae9de72ae000e..86efe1107c3b96ba8d62a492309eef9c7f97f8c8 100644 (file)
-#
-# Copyright (C) 1997-1999 Sensus Consulting Ltd. All Rights Reserved.
+# mk4vfs.tcl -- Mk4tcl Virtual File System driver
+# Copyright (C) 1997-2001 Sensus Consulting Ltd. All Rights Reserved.
 # Matt Newman <matt@sensus.org> and Jean-Claude Wippler <jcw@equi4.com>
 #
-# $Header$
+# $Id$
 #
 
-###############################################################################
-# use Pink for zip and md5 replacements, this avoids the dependency on Trf
-
-    package ifneeded Trf 1.3 {
-    
-        package require pink
-        package provide Trf 1.3
-    
-        proc zip {flag value data} {
-            switch -glob -- "$flag $value" {
-            {-mode d*} {
-                set mode decompress
-            }
-            {-mode c*} {
-                set mode compress
-            }
-            default {
-                error "usage: zip -mode {compress|decompress} data"
-            }
-            }
-            return [pink zlib $mode $data]
-        }
-    
-        proc crc {data} {
-            return [pink zlib crc32 $data]
-        }
-    
-        proc md5 {data} {
-            set cmd [pink md5]
-            $cmd update $data
-            set result [$cmd digest]
-            rename $cmd ""
-            return $result
-        }
+# uses Pink for zip and md5 replacements, this avoids the dependency on Trf
+
+  package ifneeded Trf 1.3 {
+    package require pink
+    package provide Trf 1.3
+  
+    proc zip {flag value data} {
+      switch -glob -- "$flag $value" {
+       {-mode d*} { set mode decompress }
+       {-mode c*} { set mode compress }
+       default    { error "usage: zip -mode {compress|decompress} data" }
+      }
+      return [pink zlib $mode $data]
+    }
+  
+    proc crc {data} {
+      return [pink zlib crc32 $data]
+    }
+  
+    proc md5 {data} {
+      set cmd [pink md5]
+      $cmd update $data
+      set result [$cmd digest]
+      rename $cmd ""
+      return $result
     }
+  }
 
-###############################################################################
 # this replacement is for memchan, used for simple (de)compression
 
-    package ifneeded memchan 0.1 {
-    
-           package require rechan
-           package provide memchan 0.1
-       
-           proc _memchan_handler {cmd fd args} {
-               upvar #0 ::_memchan_buf($fd) _buf
-               upvar #0 ::_memchan_pos($fd) _pos
-               set arg1 [lindex $args 0]
-               
-               switch -- $cmd {
-                   seek {
-                       switch [lindex $args 1] {
-                           1 - current { incr arg1 $_pos }
-                           2 - end { incr arg1 [string length $_buf]}
-                       }
-                       return [set _pos $arg1]
-                   }
-                   read {
-                       set r [string range $_buf $_pos [expr { $_pos + $arg1 - 1 }]]
-                       incr _pos [string length $r]
-                       return $r
-                   }
-                   write {
-                       set n [string length $arg1]
-                       if { $_pos >= [string length $_buf] } {
-                           append _buf $arg1
-                       } else { # the following doesn't work yet :(
-                           set last [expr { $_pos + $n - 1 }]
-                           set _buf [string replace $_buf $_pos $last $arg1]
-                           error "mk4vfs: sorry no inline write yet"
-                       }
-                       incr _pos $n
-                       return $n
-                   }
-                   close {
-                       unset _buf _pos
-                   }
-                   default {
-                       error "Bad call to memchan replacement handler: $cmd"
-                   }
-               }
-           }
-           
-           proc memchan {} {
-               set fd [rechan _memchan_handler 6]
-               #fconfigure $fd -translation binary -encoding binary
-               
-               set ::_memchan_buf($fd) ""
-               set ::_memchan_pos($fd) 0
-               
-               return $fd
-           }
+  package ifneeded Memchan 0.1 {
+    package require rechan
+    package provide Memchan 0.1
+  
+    proc _memchan_handler {cmd fd args} {
+      upvar #0 ::_memchan_buf($fd) _buf
+      upvar #0 ::_memchan_pos($fd) _pos
+      set arg1 [lindex $args 0]
+      
+      switch -- $cmd {
+       seek {
+         switch [lindex $args 1] {
+           1 - current { incr arg1 $_pos }
+           2 - end { incr arg1 [string length $_buf]}
+         }
+         return [set _pos $arg1]
        }
-        
-###############################################################################
-
-package require Mk4tcl
-package require vfs
-package require vfslib
-
+       read {
+         set r [string range $_buf $_pos [expr { $_pos + $arg1 - 1 }]]
+         incr _pos [string length $r]
+         return $r
+       }
+       write {
+         set n [string length $arg1]
+         if { $_pos >= [string length $_buf] } {
+           append _buf $arg1
+         } else { # the following doesn't work yet :(
+           set last [expr { $_pos + $n - 1 }]
+           set _buf [string replace $_buf $_pos $last $arg1]
+           error "mk4vfs: sorry no inline write yet"
+         }
+         incr _pos $n
+         return $n
+       }
+       close {
+         unset _buf _pos
+       }
+       default {
+         error "Bad call to memchan replacement handler: $cmd"
+       }
+      }
+    }
+    
+    proc memchan {} {
+      set fd [rechan _memchan_handler 6]
+      #fconfigure $fd -translation binary -encoding binary
+      
+      set ::_memchan_buf($fd) ""
+      set ::_memchan_pos($fd) 0
+      
+      return $fd
+    }
+  }
+    
 namespace eval vfs::mk4 {}
 
 proc vfs::mk4::Mount {what local args} {
-    set db [eval [list ::mk4vfs::_mount $what $local] $args]
-
-    ::vfs::filesystem mount $what [list ::vfs::mk4::handler $db]
-    # Register command to unmount
-    ::vfs::RegisterMount $local [list ::vfs::mk4::Unmount $db]
-    return $db
+  set db [eval [list ::mk4vfs::_mount $what $local] $args]
+  ::vfs::filesystem mount $what [list ::vfs::mk4::handler $db]
+  ::vfs::RegisterMount $local [list ::vfs::mk4::Unmount $db]
+  return $db
 }
 
 proc vfs::mk4::Unmount {db local} {
-    vfs::filesystem unmount $local
-    ::mk4vfs::_umount $db
+  vfs::filesystem unmount $local
+  ::mk4vfs::_umount $db
 }
 
 proc vfs::mk4::handler {db cmd root relative actualpath args} {
-    #tclLog [list $db $cmd $root $relative $actualpath $args]
-    if {$cmd == "matchindirectory"} {
-       eval [list $cmd $db $relative $actualpath] $args
-    } elseif {$cmd == "fileattributes"} {
-       eval [list $cmd $db $root $relative] $args
-    } else {
-       eval [list $cmd $db $relative] $args
-    }
+  if {$cmd == "matchindirectory"} {
+    eval [list $cmd $db $relative $actualpath] $args
+  } elseif {$cmd == "fileattributes"} {
+    eval [list $cmd $db $root $relative] $args
+  } else {
+    eval [list $cmd $db $relative] $args
+  }
 }
 
 proc vfs::mk4::utime {db path actime modtime} {
-    #::vfs::log [list utime $path]
-    ::mk4vfs::stat $db $path sb
-    
-    if { $sb(type) == "file" } {
-       ::mk::set $sb(ino) date $modtime
-    }
+  ::mk4vfs::stat $db $path sb
+  
+  if { $sb(type) == "file" } {
+    ::mk::set $sb(ino) date $modtime
+  }
 }
 
 # If we implement the commands below, we will have a perfect
 # virtual file system for zip files.
 
 proc vfs::mk4::matchindirectory {db path actualpath pattern type} {
-    #::vfs::log [list matchindirectory $path $actualpath $pattern $type]
-    set newres [list]
-    if {![string length $pattern]} {
-       # check single file
-       set res [list $path]
-    } else {
-       set res [::mk4vfs::getdir $db $path $pattern]
-    }
-    #::vfs::log "got $res"
-    foreach p [::vfs::matchCorrectTypes $type $res $actualpath] {
-       lappend newres "$actualpath$p"
-    }
-    #::vfs::log "got $newres"
-    return $newres
+  set newres [list]
+  if {![string length $pattern]} {
+    # check single file
+    set res [list $actualpath]
+    set actualpath ""
+  } else {
+    set res [::mk4vfs::getdir $db $path $pattern]
+  }
+  foreach p [::vfs::matchCorrectTypes $type $res $actualpath] {
+    lappend newres "$actualpath$p"
+  }
+  return $newres
 }
 
 proc vfs::mk4::stat {db name} {
-    #::vfs::log "stat $name"
-    ::mk4vfs::stat $db $name sb
-    #::vfs::log [array get sb]
+  ::mk4vfs::stat $db $name sb
 
-    # for new vfs:
-    set sb(ino) 0
-    array get sb
+  set sb(ino) 0
+  array get sb
 }
 
 proc vfs::mk4::access {db name mode} {
-    #::vfs::log "mk4-access $name $mode"
-    # This needs implementing better.  
-    switch -- $mode {
-       0 {
-           # exists
-           if {![catch {::mk4vfs::stat $db $name sb}]} {
-               return
-           }
-       }
-       1 {
-           # executable
-           if {![catch {::mk4vfs::stat $db $name sb}]} {
-               return
-           }
-       }
-       2 {
-           # writable
-           if {![catch {::mk4vfs::stat $db $name sb}]} {
-               return
-           }
-       }
-       4 {
-           # readable
-           if {![catch {::mk4vfs::stat $db $name sb}]} {
-               return
-           }
-       }
-    }
-    #tclLog "access bad"
-    error "bad file" 
+  # This needs implementing better.  
+  ::mk4vfs::stat $db $name sb
 }
 
 proc vfs::mk4::open {db file mode permissions} {
-    #::vfs::log "open $file $mode $permissions"
-    # return a list of two elements:
-    # 1. first element is the Tcl channel name which has been opened
-    # 2. second element (optional) is a command to evaluate when
-    #    the channel is closed.
-    switch -glob -- $mode {
-       {}  -
-       r   {
-           ::mk4vfs::stat $db $file sb
-       
-           if { $sb(csize) != $sb(size) } {
-               package require Trf
-               package require memchan
-               #tclLog "$file: decompressing on read"
-
-               set fd [memchan]
-               fconfigure $fd -translation binary
-               set s [mk::get $sb(ino) contents]
-               puts -nonewline $fd [zip -mode decompress $s]
-
-               fconfigure $fd -translation auto
-               seek $fd 0
-               return [list $fd [list _memchan_handler close $fd]]
-           } elseif { $::mk4vfs::direct } {
-               package require Trf
-               package require memchan
-
-               set fd [memchan]
-               fconfigure $fd -translation binary
-               puts -nonewline $fd [mk::get $sb(ino) contents]
-
-               fconfigure $fd -translation auto
-               seek $fd 0
-               return [list $fd [list _memchan_handler close $fd]]
-           } else {
-               set fd [mk::channel $sb(ino) contents r]
-           }
-           return [list $fd]
-       }
-       a   {
-           if { [catch {::mk4vfs::stat $db $file sb }] } {
-               #tclLog "stat failed - creating $file"
-               # Create file
-               ::mk4vfs::stat $db [file dirname $file] sb
-
-               set cur [mk::row append $sb(ino).files name [file tail $file] size 0 date [clock seconds] ]
-               set sb(ino) $cur
-
-               if { [string match *z* $mode] || ${mk4vfs::compress} } {
-                   set sb(csize) -1    ;# HACK - force compression
-               } else {
-                   set sb(csize) 0
-               }
-           }
-
-           if { $sb(csize) != $sb(size) } {
-               package require Trf
-               package require memchan
-
-               #tclLog "$file: compressing on append"
-               append mode z
-               set fd [memchan]
-
-               fconfigure $fd -translation binary
-               set s [mk::get $sb(ino) contents]
-               puts -nonewline $fd [zip -mode decompress $s]
-               fconfigure $fd -translation auto
-           } else {
-               set fd [mk::channel $sb(ino) contents a]
-           }
-           return [list $fd [list mk4vfs::do_close $fd $mode $sb(ino)]]
-       }
-       w*  {
-           if { [catch {::mk4vfs::stat $db $file sb }] } {
-               #tclLog "stat failed - creating $file"
-               # Create file
-               ::mk4vfs::stat $db [file dirname $file] sb
-               set cur [mk::row append $sb(ino).files name [file tail $file] size 0 date [clock seconds] ]
-               set sb(ino) $cur
-           }
-           if { [string match *z* $mode] || ${mk4vfs::compress} } {
-               package require Trf
-               package require memchan
-               #tclLog "$file: compressing on write"
-               ###zip -attach $fd -mode compress
-               append mode z
-               set fd [memchan]
-           } else {
-               set fd [mk::channel $sb(ino) contents w]
-           }
-           return [list $fd [list mk4vfs::do_close $fd $mode $sb(ino)]]
-       }
-       default     {
-           error "illegal access mode \"$mode\""
+  # return a list of two elements:
+  # 1. first element is the Tcl channel name which has been opened
+  # 2. second element (optional) is a command to evaluate when
+  #  the channel is closed.
+  switch -glob -- $mode {
+    {}  -
+    r {
+      ::mk4vfs::stat $db $file sb
+    
+      if { $sb(csize) != $sb(size) } {
+        package require Trf
+        package require Memchan
+
+        set fd [memchan]
+        fconfigure $fd -translation binary
+        set s [mk::get $sb(ino) contents]
+        puts -nonewline $fd [zip -mode decompress $s]
+
+        fconfigure $fd -translation auto
+        seek $fd 0
+        return [list $fd [list _memchan_handler close $fd]]
+      } elseif { $::mk4vfs::direct } {
+        package require Trf
+        package require Memchan
+
+        set fd [memchan]
+        fconfigure $fd -translation binary
+        puts -nonewline $fd [mk::get $sb(ino) contents]
+
+        fconfigure $fd -translation auto
+        seek $fd 0
+        return [list $fd [list _memchan_handler close $fd]]
+      } else {
+        set fd [mk::channel $sb(ino) contents r]
+      }
+      return [list $fd]
+    }
+    a {
+      if { [catch {::mk4vfs::stat $db $file sb }] } {
+        # Create file
+        ::mk4vfs::stat $db [file dirname $file] sb
+       set tail [file tail $file]
+        set fview $sb(ino).files
+        if {[info exists mk4vfs::v::fcache($fview)]} {
+         lappend mk4vfs::v::fcache($fview) $tail
+        }
+       set now [clock seconds]
+        set sb(ino) [mk::row append $fview name $tail size 0 date $now ]
+
+        if { [string match *z* $mode] || $mk4vfs::compress } {
+          set sb(csize) -1  ;# HACK - force compression
+        } else {
+          set sb(csize) 0
+        }
+      }
+
+      if { $sb(csize) != $sb(size) } {
+        package require Trf
+        package require Memchan
+
+        append mode z
+        set fd [memchan]
+
+        fconfigure $fd -translation binary
+        set s [mk::get $sb(ino) contents]
+        puts -nonewline $fd [zip -mode decompress $s]
+        fconfigure $fd -translation auto
+      } else {
+        set fd [mk::channel $sb(ino) contents a]
+      }
+      return [list $fd [list mk4vfs::do_close $fd $mode $sb(ino)]]
+    }
+    w*  {
+      if { [catch {::mk4vfs::stat $db $file sb }] } {
+        # Create file
+        ::mk4vfs::stat $db [file dirname $file] sb
+       set tail [file tail $file]
+        set fview $sb(ino).files
+        if {[info exists mk4vfs::v::fcache($fview)]} {
+         lappend mk4vfs::v::fcache($fview) $tail
        }
+       set now [clock seconds]
+        set sb(ino) [mk::row append $fview name $tail size 0 date $now ]
+      }
+
+      if { [string match *z* $mode] || $mk4vfs::compress } {
+        package require Trf
+        package require Memchan
+        append mode z
+        set fd [memchan]
+      } else {
+        set fd [mk::channel $sb(ino) contents w]
+      }
+      return [list $fd [list mk4vfs::do_close $fd $mode $sb(ino)]]
+    }
+    default   {
+      error "illegal access mode \"$mode\""
     }
+  }
 }
 
 proc vfs::mk4::createdirectory {db name} {
-    #::vfs::log "createdirectory $name"
-    mk4vfs::mkdir $db $name
+  mk4vfs::mkdir $db $name
 }
 
 proc vfs::mk4::removedirectory {db name} {
-    #::vfs::log "removedirectory $name"
-    mk4vfs::delete $db $name
+  mk4vfs::delete $db $name
 }
 
 proc vfs::mk4::deletefile {db name} {
-    #::vfs::log "deletefile $name"
-    mk4vfs::delete $db $name
+  mk4vfs::delete $db $name
 }
 
 proc vfs::mk4::fileattributes {db root relative args} {
-    #::vfs::log "fileattributes $args"
-    switch -- [llength $args] {
-       0 {
-           # list strings
-           return [::vfs::listAttributes]
-       }
-       1 {
-           # get value
-           set index [lindex $args 0]
-           return [::vfs::attributesGet $root $relative $index]
+  switch -- [llength $args] {
+    0 {
+      # list strings
+      return [::vfs::listAttributes]
+    }
+    1 {
+      # get value
+      set index [lindex $args 0]
+      return [::vfs::attributesGet $root $relative $index]
 
-       }
-       2 {
-           # set value
-           set index [lindex $args 0]
-           set val [lindex $args 1]
-           return [::vfs::attributesSet $root $relative $index $val]
-       }
     }
+    2 {
+      # set value
+      set index [lindex $args 0]
+      set val [lindex $args 1]
+      return [::vfs::attributesSet $root $relative $index $val]
+    }
+  }
 }
 
+package require Mk4tcl
+package require vfs
+package require vfslib
+
 package provide mk4vfs 1.0
 
 namespace eval mk4vfs {
-    variable uid 0
-    variable compress 1         ;# HACK - needs to be part of "Super-Block"
-    variable flush      5000    ;# Auto-Commit frequency
-    variable direct 0
+  variable compress 1     ;# HACK - needs to be part of "Super-Block"
+  variable flush    5000  ;# Auto-Commit frequency
+  variable direct   0    ;# read through a memchan, or from Mk4tcl if zero
+
+  namespace eval v {
+    variable seq      0
 
-    namespace export mount umount
+    array set cache {}
+    array set fcache {}
+  }
+
+  namespace export mount umount
 }
 
 proc mk4vfs::init {db} {
-    mk::view layout $db.dirs {name:S parent:I {files {name:S size:I date:I contents:M}}}
+  mk::view layout $db.dirs {name:S parent:I {files {name:S size:I date:I contents:M}}}
 
-    if { [mk::view size $db.dirs] == 0 } {
-        mk::row append $db.dirs name <root> parent 0
-    }
+  if { [mk::view size $db.dirs] == 0 } {
+    mk::row append $db.dirs name <root> parent 0
+  }
+
+  # 2001-12-13: use parent -1 for root level!
+  mk::set $db.dirs!0 parent -1
 }
 
 proc mk4vfs::mount {args} {
-    uplevel ::vfs::mk4::Mount $args
+  uplevel ::vfs::mk4::Mount $args
 }
 
 proc mk4vfs::_mount {path file args} {
-    variable uid
-    set db mk4vfs[incr uid]
+  set db mk4vfs[incr v::seq]
 
-    eval [list mk::file open $db $file] $args
+  eval [list mk::file open $db $file] $args
 
-    init $db
+  init $db
 
-    set flush 1
-    for {set idx 0} {$idx < [llength $args]} {incr idx} {
-        switch -- [lindex $args $idx] {
-        -readonly       -
-        -nocommit       {set flush 0}
-        }
+  set flush 1
+  for {set idx 0} {$idx < [llength $args]} {incr idx} {
+    switch -- [lindex $args $idx] {
+      -readonly   -
+      -nocommit   {set flush 0}
     }
-    if { $flush } {
-        _commit $db
-    }
-    return $db
+  }
+  if { $flush } {
+    _commit $db
+  }
+  return $db
 }
 
 proc mk4vfs::_commit {db} {
-    after ${::mk4vfs::flush} [list mk4vfs::_commit $db]
-    mk::file commit $db
+  variable flush
+  after $flush [list mk4vfs::_commit $db]
+  mk::file commit $db
 }
 
 proc mk4vfs::umount {local} {
-    foreach {db path} [mk::file open] {
-      if {[string equal $local $path]} {
-       uplevel ::vfs::mk4::Unmount $db $local
-       return
-      }
+  foreach {db path} [mk::file open] {
+    if {[string equal $local $path]} {
+      uplevel ::vfs::mk4::Unmount $db $local
+      return
     }
-    tclLog "umount $local? [mk::file open]"
+  }
+  tclLog "umount $local? [mk::file open]"
 }
 
 proc mk4vfs::_umount {db} {
-    after cancel [list mk4vfs::_commit $db]
-    variable cache
-    array unset cache $db.*
-    #tclLog [list unmount $db]
-    mk::file close $db
+  after cancel [list mk4vfs::_commit $db]
+  array unset v::cache $db,*
+  array unset v::fcache $db.*
+  mk::file close $db
 }
 
 proc mk4vfs::stat {db path arr} {
-    variable cache
-    
-    #set pre [array names cache]
-    
-    upvar 1 $arr sb
-    #tclLog "mk4vfs::stat $db $path $arr"
-
-    set sp [::file split $path]
-    set tail [lindex $sp end]
-
-    set parent 0
-    set view $db.dirs
-    set cur $view!$parent
-    set type directory
+  upvar 1 $arr sb
 
-    foreach ele [lrange $sp 0 [expr { [llength $sp] - 2 }]] {
+  set sp [::file split $path]
+  set tail [lindex $sp end]
 
-        if { [info exists cache($cur,$ele)] } {
-            set parent $cache($cur,$ele)
-        } else {
-            #set row [mk::select $view name $ele parent $parent]
-            set row [find/dir $view $ele $parent]
-
-            if { $row == -1 } {
-                #tclLog "select failed: parent $parent name $ele"
-                return -code error "could not read \"$path\": no such file or directory"
-            }
-           set parent $row
-            set cache($cur,$ele) $parent
-        }
-       set cur $view!$parent
-       #mk::cursor position cur $parent
-    }
-    #
-    # Now check if final comp is a directory or a file
-    #
-    # CACHING is required - it can deliver a x15 speed-up!
-    #
-    if { [string equal $tail "."] || [string equal $tail ":"] || [string equal $tail ""] } {
-       # donothing
-
-    } elseif { [info exists cache($cur,$tail)] } {
-        set type directory
-        #set cur $view!$cache($cur,$tail)
-       mk::cursor position cur $cache($cur,$tail)
+  set parent 0
+  set view $db.dirs
+  set type directory
 
+  foreach ele [lrange $sp 0 end-1] {
+    if {[info exists v::cache($db,$parent,$ele)]} {
+      set parent $v::cache($db,$parent,$ele)
     } else {
-        # File?
-        #set row [mk::select $cur.files name $tail]
-        set row [find/file $cur.files $tail]
-
-        if { $row != -1 } {
-            set type file
-            set view $cur.files
-           #set cur $view!$row
-           mk::cursor create cur $view $row
-
-        } else {
-            # Directory?
-            #set row [mk::select $view parent $parent name $tail]
-            set row [find/dir $view $tail $parent]
-
-            if { $row != -1 } {
-                set type directory
-               #set cur $view!$row
-               # MUST SET cache BEFORE calling mk::cursor!!!
-               set cache($cur,$tail) $row
-               mk::cursor position cur $row
-            } else { 
-                return -code error "could not read \"$path\": no such file or directory"
-            }
-        }
+      set row [mk::select $view -count 1 parent $parent name $ele]
+      if { $row == "" } {
+        return -code error "could not read \"$path\": no such file or directory"
+      }
+      set v::cache($db,$parent,$ele) $row
+      set parent $row
     }
-    set sb(type)       $type
-    set sb(view)       $view
-    set sb(ino)                $cur
-
-    if { [string equal $type "directory"] } {
-        set sb(atime)   0
-        set sb(ctime)   0
-       set sb(gid)     0
-        set sb(mode)    0777
-        set sb(mtime)   0
-        set sb(nlink)   [expr { [mk::get $cur files] + 1 }]
-        set sb(size)    0
-        set sb(csize)   0
-       set sb(uid)     0
+  }
+  
+  # Now check if final comp is a directory or a file
+  # CACHING is required - it can deliver a x15 speed-up!
+  
+  if { [string equal $tail "."] || [string equal $tail ":"] ||
+                                       [string equal $tail ""] } {
+    set row $parent
+
+  } elseif { [info exists v::cache($db,$parent,$tail)] } {
+    set row $v::cache($db,$parent,$tail)
+  } else {
+    # File?
+    set fview $view!$parent.files
+    # create a name cache of files in this directory
+    if {![info exists v::fcache($fview)]} {
+      # cache only a limited number of directories
+      if {[array size v::fcache] >= 10} {
+        array unset v::fcache *
+      }
+      set v::fcache($fview) {}
+      mk::loop c $fview {
+        lappend v::fcache($fview) [mk::get $c name]
+      }
+    }
+    set row [lsearch -exact $v::fcache($fview) $tail]
+    #set row [mk::select $fview -count 1 name $tail]
+    #if {$row == ""} { set row -1 }
+    if { $row != -1 } {
+      set type file
+      set view $view!$parent.files
     } else {
-        set mtime      [mk::get $cur date]
-        set sb(atime)  $mtime
-        set sb(ctime)  $mtime
-       set sb(gid)     0
-        set sb(mode)    0777
-        set sb(mtime)  $mtime
-        set sb(nlink)   1
-        set sb(size)    [mk::get $cur size]
-        set sb(csize)   [mk::get $cur -size contents]
-       set sb(uid)     0
+      # Directory?
+      set row [mk::select $view -count 1 parent $parent name $tail]
+      if { $row != "" } {
+        set v::cache($db,$parent,$tail) $row
+      } else { 
+        return -code error "could not read \"$path\": no such file or directory"
+      }
     }
-    
-    #foreach n [array names cache] {
-    #if {[lsearch -exact $pre $n] == -1} {
-    #::vfs::log "added $path $n $cache($n)"
-    #}
-    #}
+  }
+  set cur $view!$row
+
+  set sb(type)    $type
+  set sb(view)    $view
+  set sb(ino)     $cur
+
+  if { [string equal $type "directory"] } {
+    set sb(atime) 0
+    set sb(ctime) 0
+    set sb(gid)   0
+    set sb(mode)  0777
+    set sb(mtime) 0
+    set sb(nlink) [expr { [mk::get $cur files] + 1 }]
+    set sb(size)  0
+    set sb(csize) 0
+    set sb(uid)   0
+  } else {
+    set mtime   [mk::get $cur date]
+    set sb(atime) $mtime
+    set sb(ctime) $mtime
+    set sb(gid)   0
+    set sb(mode)  0777
+    set sb(mtime) $mtime
+    set sb(nlink) 1
+    set sb(size)  [mk::get $cur size]
+    set sb(csize) [mk::get $cur -size contents]
+    set sb(uid)   0
+  }
 }
 
 proc mk4vfs::do_close {fd mode cur} {
-    # Set size to -1 before the seek - just in case it fails.
-    
-    if {[catch {
-       set iswrite [regexp {[aw]} $mode]
-           
-       if {$iswrite} {
-           mk::set $cur size -1 date [clock seconds]
-           flush $fd
-           if { [string match *z* $mode] } {
-               fconfigure $fd -translation binary
-               seek $fd 0
-               set data [read $fd]
-               _memchan_handler close $fd
-               set cdata [zip -mode compress $data]
-               set len [string length $data]
-               set clen [string length $cdata]
-               if { $clen < $len } {
-                   mk::set $cur size $len contents $cdata
-               } else {
-                   mk::set $cur size $len contents $data
-               }
-           } else {
-               mk::set $cur size [mk::get $cur -size contents]
-           }
-           # added 30-10-2000
-           set db [lindex [split $cur .] 0]
-           mk::file autocommit $db
-       } else {
-           # This should only be called for write operations...
-           error "Shouldn't call me for read ops"
-       }
-    } err]} {
-       global errorInfo
-       tclLog "mk4vfs::do_close callback error: $err $errorInfo"
-###!!! return -code error $err
+  # Set size to -1 before the seek - just in case it fails.
+  
+  if {[catch {
+    set iswrite [regexp {[aw]} $mode]
+      
+    if {$iswrite} {
+      mk::set $cur size -1 date [clock seconds]
+      flush $fd
+      if { [string match *z* $mode] } {
+        fconfigure $fd -translation binary
+        seek $fd 0
+        set data [read $fd]
+        # this was a duplicate close!!! 12-10-2001
+        #close $fd
+        _memchan_handler close $fd
+        set cdata [zip -mode compress $data]
+        set len [string length $data]
+        set clen [string length $cdata]
+        if { $clen < $len } {
+          mk::set $cur size $len contents $cdata
+        } else {
+          mk::set $cur size $len contents $data
+        }
+      } else {
+        mk::set $cur size [mk::get $cur -size contents]
+      }
+      # added 30-10-2000
+      set db [lindex [split $cur .] 0]
+      mk::file autocommit $db
+    } else {
+      # This should only be called for write operations...
+      error "Shouldn't call me for read ops"
     }
+  } err]} {
+    global errorInfo
+    tclLog "mk4vfs::do_close callback error: $err $errorInfo"
+###!!!  return -code error $err
+  }
 }
 
 proc mk4vfs::mkdir {db path} {
-    set sp [::file split $path]
-    set parent 0
-    set view $db.dirs
-
-    set npath {}
-    foreach ele $sp {
-        set npath [file join $npath $ele]
-
-        if { ![catch {stat $db $npath sb}] } {
-            if { $sb(type) != "directory" } {
-                return -code error "can't create directory \"$npath\": file already exists"
-            }
-            set parent [mk::cursor position sb(ino)]
-            continue
-        }
-        #set parent [mk::cursor position sb(ino)]
-#puts "set cur \[mk::row append $view name $ele parent $parent]"
-        set cur [mk::row append $view name $ele parent $parent]
-        set parent [mk::cursor position cur]
-    }
-}
+  set sp [::file split $path]
+  set parent 0
+  set view $db.dirs
 
-# removed this from 'getdir' proc.
-if { 0 } {
-    foreach row [mk::select $sb(view) parent $parent -glob name $pat] {
-       if { $row == 0 } {continue}
+  set npath {}
+  foreach ele $sp {
+    set npath [file join $npath $ele]
 
-       set hits([mk::get $sb(view)!$row name]) 1
-    }
-    # Match files
-    set view $sb(view)!$parent.files
-    foreach row [mk::select $view -glob name $pat] {
-       set hits([mk::get $view!$row name]) 1
+    if { ![catch {stat $db $npath sb}] } {
+      if { $sb(type) != "directory" } {
+        return -code error "can't create directory \"$npath\": file already exists"
+      }
+      set parent [mk::cursor position sb(ino)]
+      continue
     }
-} 
+    #set parent [mk::cursor position sb(ino)]
+    set cur [mk::row append $view name $ele parent $parent]
+    set parent [mk::cursor position cur]
+  }
+}
 
 proc mk4vfs::getdir {db path {pat *}} {
-    #tclLog [list mk4vfs::getdir $db $path $pat]
-
-    if { [catch {
-        stat $db $path sb
-    }] } {
-        return {}
-    }
-
-    if { $sb(type) != "directory" } {
-        return {}
-        #return -code error "bad path \"$path\": not a directory"
-    }
-    # Match directories
-    set parent [mk::cursor position sb(ino)] 
-    mk::loop sb(ino) {
-       if { [mk::get $sb(ino) parent] == $parent &&
-            [string match $pat [mk::get $sb(ino) name]] &&
-            [mk::cursor position sb(ino)] != 0 } {
-           set hits([mk::get $sb(ino) name]) 1
-       }
-    }
-    # Match files
-    mk::loop sb(ino) $sb(view)!$parent.files {
-       if { [string match $pat [mk::get $sb(ino) name]] } {
-           set hits([mk::get $sb(ino) name]) 1
-       }
-    }
-    return [lsort [array names hits]]
+  if {[catch { stat $db $path sb }] || $sb(type) != "directory" } {
+    return
+  }
+
+  # Match directories
+  set parent [mk::cursor position sb(ino)] 
+  foreach row [mk::select $sb(view) parent $parent -glob name $pat] {
+    set hits([mk::get $sb(view)!$row name]) 1
+  }
+  # Match files
+  set view $sb(view)!$parent.files
+  foreach row [mk::select $view -glob name $pat] {
+    set hits([mk::get $view!$row name]) 1
+  }
+  return [lsort [array names hits]]
 }
 
 proc mk4vfs::mtime {db path time} {
 
-    stat $db $path sb
+  stat $db $path sb
 
-    if { $sb(type) == "file" } {
-        mk::set $sb(ino) date $time
-    }
-    return $time
+  if { $sb(type) == "file" } {
+    mk::set $sb(ino) date $time
+  }
+  return $time
 }
 
 proc mk4vfs::delete {db path {recursive 0}} {
-    #tclLog "trying to delete $path"
-    set rc [catch { stat $db $path sb } err]
-    if { $rc }  {
-       #tclLog "delete error: $err"
-       return -code error $err
+  stat $db $path sb
+  if {$sb(type) == "file" } {
+    mk::row delete $sb(ino)
+    if {[regexp {(.*)!(\d+)} $sb(ino) - v r] && [info exists v::fcache($v)]} {
+      set v::fcache($v) [lreplace $v::fcache($v) $r $r]
     }
-    if {$sb(type) == "file" } {
-       mk::row delete $sb(ino)
+  } else {
+    # just mark dirs as deleted
+    set contents [getdir $db $path *]
+    if {$recursive} {
+      # We have to delete these manually, else
+      # they (or their cache) may conflict with
+      # something later
+      foreach f $contents {
+        delete $db [file join $path $f] $recursive
+      }
     } else {
-       # just mark dirs as deleted
-       set contents [getdir $db $path *]
-       #puts "path, $contents"
-       if {$recursive} {
-           # We have to delete these manually, else
-           # they (or their cache) may conflict with
-           # something later
-           foreach f $contents {
-               delete $db [file join $path $f] $recursive
-           }
-       } else {
-           if {[llength $contents]} {
-               return -code error "Non-empty"
-           }
-       }
-       set tail [file tail $path]
-       variable cache
-       set var2 "$sb(view)![mk::get $sb(ino) parent],$tail"
-       #puts "del $path, $tail , $var2, [info exists cache($var2)]"
-       if {[info exists cache($var2)]} {
-           #puts "remove2: $path $var2 $cache($var2)"
-           unset cache($var2)
-       }
-       
-       mk::set $sb(ino) parent -1
-    }
-    return ""
-}
-
-proc mk4vfs::find/file {v name} {
-    mk::loop cur $v {
-       if { [string equal [mk::get $cur name] $name] } {
-           return [mk::cursor position cur]
-       }
-    }
-    return -1
-}
-
-proc mk4vfs::find/dir {v name parent} {
-    mk::loop cur $v {
-       if {    [mk::get $cur parent] == $parent &&
-               [string equal [mk::get $cur name] $name] } {
-           return [mk::cursor position cur]
-       }
+      if {[llength $contents]} {
+        return -code error "Non-empty"
+      }
     }
-    return -1
+    array unset v::cache "$db,[mk::get $sb(ino) parent],[file tail $path]"
+    
+    mk::set $sb(ino) parent -1 name ""
+  }
+  return ""
 }
index 43f69b09d25c4113895e426256127b64dd938f21..2cade5066255e4b9f12818467e16c23c64252c39 100644 (file)
@@ -16,6 +16,7 @@ proc vfs::ns::Mount {ns local} {
     ::vfs::log "ns $ns mounted at $local"
     vfs::filesystem mount $local [list vfs::ns::handler $ns]
     vfs::RegisterMount $local [list vfs::ns::Unmount]
+    return $local
 }
 
 proc vfs::ns::Unmount {local} {
index a2b2fb39765074f061ec2c2789afe7019af23b8f..b972084de06f60785d171ea73a7ca4ad4b41d979 100644 (file)
@@ -20,8 +20,6 @@ package provide vfslib 0.1
 # when I might not have the history procedures loaded yet!
 #proc history {args} {}
 
-lappend auto_path [file dirname [info script]]
-
 # This stuff is for TclKit
 namespace eval ::vfs {
     variable temp
index 98d0689be691e56e5db1f5d7bec8391856fb5ab9..d68c9af39ed5fd9265faad44dcee9538d341b7d9 100644 (file)
@@ -30,7 +30,6 @@ proc vfs::zip::Unmount {fd local} {
 
 proc vfs::zip::handler {zipfd cmd root relative actualpath args} {
     #::vfs::log [list $zipfd $cmd $root $relative $actualpath $args]
-    #update
     if {$cmd == "matchindirectory"} {
        eval [list $cmd $zipfd $relative $actualpath] $args
     } else {
@@ -48,6 +47,11 @@ proc vfs::zip::matchindirectory {zipfd path actualpath pattern type} {
     # for the existence of a single file $path only
     set res [::zip::getdir $zipfd $path $pattern]
     #::vfs::log "got $res"
+    if {![string length $pattern]} {
+       set res [list $actualpath]
+       set actualpath ""
+    }
+
     set newres [list]
     foreach p [::vfs::matchCorrectTypes $type $res $actualpath] {
        lappend newres "$actualpath$p"
@@ -441,7 +445,7 @@ proc zip::stat {fd path arr} {
 
 # Treats empty pattern as asking for a particular file only
 proc zip::getdir {fd path {pat *}} {
-#    ::vfs::log [list getdir $fd $path $pat]
+    #::vfs::log [list getdir $fd $path $pat]
     upvar #0 zip::$fd.toc toc
 
     if { $path == "." || $path == "" } {
@@ -454,26 +458,33 @@ proc zip::getdir {fd path {pat *}} {
     }
     set depth [llength [file split $path]]
 
-    set ret {}
-    foreach key [array names toc $path] {
-       if {[string index $key end] == "/"} {
-           # Directories are listed twice: both with and without
-           # the trailing '/', so we ignore the one with
-           continue
-       }
-       array set sb $toc($key)
-
-       if { $sb(depth) == $depth } {
-           if {[info exists toc(${key}/)]} {
-               array set sb $toc(${key}/)
+    #puts stderr "getdir $fd $path $depth $pat [array names toc $path]"
+    if {$depth} {
+       set ret {}
+       foreach key [array names toc $path] {
+           if {[string index $key end] == "/"} {
+               # Directories are listed twice: both with and without
+               # the trailing '/', so we ignore the one with
+               continue
+           }
+           array set sb $toc($key)
+
+           if { $sb(depth) == $depth } {
+               if {[info exists toc(${key}/)]} {
+                   array set sb $toc(${key}/)
+               }
+               lappend ret [file tail $sb(name)]
+           } else {
+               #::vfs::log "$sb(depth) vs $depth for $sb(name)"
            }
-           lappend ret [file tail $sb(name)]
-       } else {
-           #::vfs::log "$sb(depth) vs $depth for $sb(name)"
+           unset sb
        }
-       unset sb
+       return $ret
+    } else {
+       # just the 'root' of the zip archive.  This obviously exists and
+       # is a directory.
+       return [list {}]
     }
-    return $ret
 }
 
 proc zip::_close {fd} {
index 315911d3b9e2c6afc1e6b410f0724c0c903b58cd..49c2e58125be911b73f6c7188b56456d3927fb53 100644 (file)
@@ -13,7 +13,7 @@ VFS_VERSION = 1.0
 DLL_VERSION = 10
 
 # comment the following line to compile with symbols
-NODEBUG=0
+NODEBUG=1
 
 !IF "$(NODEBUG)" == "1"
 DEBUGDEFINES =