better file vols
authorVince Darley <vincentdarley@sourceforge.net>
Thu, 27 Sep 2001 18:56:34 +0000 (18:56 +0000)
committerVince Darley <vincentdarley@sourceforge.net>
Thu, 27 Sep 2001 18:56:34 +0000 (18:56 +0000)
ChangeLog
doc/vfs.n
generic/vfs.c
library/mk4vfs.tcl
library/vfsUrl.tcl
library/vfsUtils.tcl
win/makefile.vc

index cea86cf54fe19462a76bece607b90c1855a79e50..78b6f1c110148df8bae07b7569a93eddb16056ed 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2001-09-27  Vince Darley <vincentdarley@sourceforge.net>
+       * library/mk4vfs.tcl: Update from Jean-Claude Wippler
+       * library/vfsUtils.tcl:
+       * generic/vfs.c: Moved 'list volumes' functionality
+       entirely into C from Tcl (faster and easier for tclkit
+       to make proper use of).  This required the addition
+       of a '-volume' flag to the mount/unmount commands, and
+       meant we can remove the 'mountschanged' subcommand.
+
 2001-09-07  Vince Darley <vincentdarley@sourceforge.net>
        * generic/vfs.c: Fixed '==' error
        * doc/vfs.n: Improved docs.
index 3e13c6b8b9639451960997402ebd7c39eb128180..cc9629bded3e27a9f8d3c314ddd9ef9a0039c536 100644 (file)
--- a/doc/vfs.n
+++ b/doc/vfs.n
@@ -21,8 +21,6 @@
 .sp
 \fBvfs::filesystem\fR \fIunmount\fR
 .sp
-\fBvfs::filesystem\fR \fImountschanged\fR
-.sp
 .sp
 \fBvfs::foo \fIa b c\fR
 .BE
@@ -52,28 +50,25 @@ control over this could be exposed to Tcl in the future).  However, the
 vfs package will at that stage not have any new filesystems mounted, so
 it will have little effect.
 .TP
-\fBvfs::filesystem\fR \fImount\fR \fIpath\fR \fIcommand\fR
+\fBvfs::filesystem\fR \fImount\fR \fI?-volume?\fR \fIpath\fR \fIcommand\fR
 To use a virtual filesystem, it must be 'mounted'.  Mounting involves
 declaring to the vfs package that any subdirectories of a given
 \fIpath\fR in the filesystem should be handled by the given \fIcommand\fR
-which should be a Tcl command or procedure.
+which should be a Tcl command or procedure.  If the \fI?-volume?\fR
+flag is given, the given mount point is also registered with Tcl as
+a new volume (like a new drive).
 .TP
-\fBvfs::filesystem\fR \fIunmount\fR \fIpath\fR 
+\fBvfs::filesystem\fR \fIunmount\fR \fI?-volume?\fR \fIpath\fR 
 This unmounts the virtual filesystem which was mounted at \fIpath\fR
 hence removing it from Tcl's filesystem, or throws an error if no
-filesystem was mounted there.
+filesystem was mounted there.  The \fI?-volume?\fR flag should be
+given if it was also given when the path was mounted.
 .TP
 \fBvfs::filesystem\fR \fIinfo\fR \fI?path?\fR
 If no arguments are given, this returns a list of all filesystems
 mounted.  If a path argument is given, then the \fIcommand\fR to be
 used for that path is returned, or an error is thrown if no vfs is
 mounted for that path.
-.TP
-\fBvfs::filesystem\fR \fImountschanged\fR
-There is generally no need to call this.  It is used by the library
-when the list of volumes changes, or when any kind of new mount point
-is added.  It ensures that Tcl's cached file representations are not
-out of date.
 .PP
 Currently mount information is stored by the extension in the
 vfs::mount array variable, but this should be considered private
index a5beb0f7803240fb0d9b8a2ddd08720116994b26..67bf52c499cfd8efc3319ad0717189dd465eb314 100644 (file)
 
 EXTERN int Vfs_Init _ANSI_ARGS_((Tcl_Interp*));
 
+static void Vfs_AddVolume _ANSI_ARGS_((Tcl_Obj*));
+static int Vfs_RemoveVolume _ANSI_ARGS_((Tcl_Obj*));
+
+/* 
+ * Stores the list of volumes registered with the vfs 
+ * (and therefore also registered with Tcl).  It is 
+ * maintained as a valid Tcl list at all times, or
+ * NULL if there are none.  To improve Tcl's efficiency,
+ * when there are no volumes, we keep this NULL rather
+ * than as an empty list.
+ */
+static Tcl_Obj *vfsVolumes = NULL;
+
 /*
  * Structure used for the native representation of a path in a Tcl vfs.
  * To fully specify a file, the string representation is also required.
@@ -241,11 +254,11 @@ VfsFilesystemObjCmd(dummy, interp, objc, objv)
     int index;
 
     static char *optionStrings[] = {
-       "info", "mount", "unmount", "volumeschanged", 
+       "info", "mount", "unmount",
        NULL
     };
     enum options {
-       VFS_INFO, VFS_MOUNT, VFS_UNMOUNT, VFS_VOLUMESCHANGED,
+       VFS_INFO, VFS_MOUNT, VFS_UNMOUNT,
     };
 
     if (objc < 2) {
@@ -260,9 +273,10 @@ VfsFilesystemObjCmd(dummy, interp, objc, objv)
     switch ((enum options) index) {
        case VFS_MOUNT: {
            Tcl_Obj * path;
+           int i;
            Tcl_Interp* vfsInterp;
-           if (objc != 4) {
-               Tcl_WrongNumArgs(interp, 1, objv, "mount path cmd");
+           if (objc < 4 || objc > 5) {
+               Tcl_WrongNumArgs(interp, 1, objv, "mount ?-volume? path cmd");
                return TCL_ERROR;
            }
            vfsInterp = (Tcl_Interp*) Tcl_FSData(&vfsFilesystem);
@@ -270,9 +284,22 @@ VfsFilesystemObjCmd(dummy, interp, objc, objv)
                Tcl_SetResult(interp, "vfs not registered", TCL_STATIC);
                return TCL_ERROR;
            }
-           path = Tcl_FSGetNormalizedPath(interp, objv[2]);
+           if (objc == 5) {
+               char *option = Tcl_GetString(objv[2]);
+               if (strcmp("-volume", option)) {
+                   Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
+                           "bad option \"", option,
+                           "\": must be -volume", (char *) NULL);
+                   return TCL_ERROR;
+               }
+               i = 3;
+               Vfs_AddVolume(objv[i]);
+           } else {
+               i = 2;
+           }
+           path = Tcl_FSGetNormalizedPath(interp, objv[i]);
            if (Tcl_SetVar2Ex(vfsInterp, "vfs::mount", 
-                             Tcl_GetString(path), objv[3], 
+                             Tcl_GetString(path), objv[i+1], 
                              TCL_LEAVE_ERR_MSG | TCL_GLOBAL_ONLY) == NULL) {
                return TCL_ERROR;
            }
@@ -310,9 +337,9 @@ VfsFilesystemObjCmd(dummy, interp, objc, objv)
        case VFS_UNMOUNT: {
            Tcl_Obj * path;
            Tcl_Interp* vfsInterp;
-           int res;
-           if (objc != 3) {
-               Tcl_WrongNumArgs(interp, 2, objv, "path");
+           int res, i;
+           if (objc < 3 || objc > 4) {
+               Tcl_WrongNumArgs(interp, 2, objv, "?-volume? path");
                return TCL_ERROR;
            }
            vfsInterp = (Tcl_Interp*) Tcl_FSData(&vfsFilesystem);
@@ -320,21 +347,29 @@ VfsFilesystemObjCmd(dummy, interp, objc, objv)
                Tcl_SetResult(interp, "vfs not registered", TCL_STATIC);
                return TCL_ERROR;
            }
-           path = Tcl_FSGetNormalizedPath(interp, objv[2]);
+           if (objc == 4) {
+               char *option = Tcl_GetString(objv[2]);
+               if (strcmp("-volume", option)) {
+                   Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
+                           "bad option \"", option,
+                           "\": must be -volume", (char *) NULL);
+                   return TCL_ERROR;
+               }
+               i = 3;
+           } else {
+               i = 2;
+           }
+           path = Tcl_FSGetNormalizedPath(interp, objv[i]);
            res = Tcl_UnsetVar2(vfsInterp, "vfs::mount", Tcl_GetString(path), 
                                TCL_LEAVE_ERR_MSG | TCL_GLOBAL_ONLY);
            if (res == TCL_OK) {
+               if (i == 3) {
+                   Vfs_RemoveVolume(objv[i]);
+               }
                Tcl_FSMountsChanged(&vfsFilesystem);
            }
            return res;
        }
-       case VFS_VOLUMESCHANGED: {
-           if (objc > 2) {
-               Tcl_WrongNumArgs(interp, 2, objv, NULL);
-               return TCL_ERROR;
-           }
-           Tcl_FSMountsChanged(&vfsFilesystem);
-       }
     }
     return TCL_OK;
 }
@@ -1107,22 +1142,54 @@ VfsUtime(pathPtr, tval)
 Tcl_Obj*
 VfsListVolumes(void)
 {
-    Tcl_Obj *resultPtr;
-    Tcl_SavedResult savedResult;
-    Tcl_Interp* interp;
-    
-    interp = (Tcl_Interp*) Tcl_FSData(&vfsFilesystem);
-    Tcl_SaveResult(interp, &savedResult);
-
-    /* List all vfs volumes */
-    if (Tcl_GlobalEval(interp, "::vfs::listVolumes") == TCL_OK) {
-       resultPtr = Tcl_DuplicateObj(Tcl_GetObjResult(interp));
-       Tcl_IncrRefCount(resultPtr);
+    if (vfsVolumes == NULL) {
+        return NULL;
     } else {
-       resultPtr = NULL;
+       Tcl_IncrRefCount(vfsVolumes);
+       return vfsVolumes;
     }
-    Tcl_RestoreResult(interp, &savedResult);
-    return resultPtr;
+}
+
+void
+Vfs_AddVolume(volume)
+    Tcl_Obj *volume;
+{
+    if (vfsVolumes == NULL) {
+        vfsVolumes = Tcl_NewObj();
+       Tcl_IncrRefCount(vfsVolumes);
+    }
+    Tcl_ListObjAppendElement(NULL, vfsVolumes, volume);
+}
+
+int
+Vfs_RemoveVolume(volume)
+    Tcl_Obj *volume;
+{
+    int i, len;
+    Tcl_ListObjLength(NULL, vfsVolumes, &len);
+    for (i = 0;i < len; i++) {
+       Tcl_Obj *vol;
+        Tcl_ListObjIndex(NULL, vfsVolumes, i, &vol);
+       if (!strcmp(Tcl_GetString(vol),Tcl_GetString(volume))) {
+           /* It's in the list, at index i */
+           if (len == 1) {
+               /* An optimization here */
+               Tcl_DecrRefCount(vfsVolumes);
+               vfsVolumes = NULL;
+           } else {
+               /* Make ourselves the unique owner */
+               if (Tcl_IsShared(vfsVolumes)) {
+                   Tcl_Obj *oldVols = vfsVolumes;
+                   vfsVolumes = Tcl_DuplicateObj(oldVols);
+                   Tcl_DecrRefCount(oldVols);
+               }
+               /* Remove the element */
+               Tcl_ListObjReplace(NULL, vfsVolumes, i, 1, 0, NULL);
+               return TCL_OK;
+           }
+       }
+    }
+    return TCL_ERROR;
 }
 
 \f
@@ -1192,5 +1259,9 @@ static
 void VfsExitProc(ClientData clientData)
 {
     Tcl_FSUnregister(&vfsFilesystem);
+    if (vfsVolumes != NULL) {
+        Tcl_DecrRefCount(vfsVolumes);
+       vfsVolumes = NULL;
+    }
 }
 
index 4e3c698119db1085b711365d62da6b815a0cc53b..2815cc0f7dc01a9b2850eb363a9d91d4b9cfc575 100644 (file)
@@ -56,9 +56,9 @@
                
                switch -- $cmd {
                    seek {
-                       switch $args {
-                           1 { incr arg1 $_pos }
-                           2 { incr arg1 [string length $_buf]}
+                       switch [lindex $args 1] {
+                           1 - current { incr arg1 $_pos }
+                           2 - end { incr arg1 [string length $_buf]}
                        }
                        return [set _pos $arg1]
                    }
@@ -112,15 +112,9 @@ proc vfs::mk4::Mount {what local args} {
     return $db
 }
 
-namespace eval mk4vfs {}
-
-proc mk4vfs::mount {args} {
-    uplevel 1 [list ::vfs::mk4::mount] $args
-}
-
 proc vfs::mk4::Unmount {db local} {
     vfs::filesystem unmount $local
-    ::mk4vfs::umount $db
+    ::mk4vfs::_umount $db
 }
 
 proc vfs::mk4::handler {db cmd root relative actualpath args} {
@@ -164,7 +158,6 @@ proc vfs::mk4::stat {db name} {
     #::vfs::log [array get sb]
 
     # for new vfs:
-    set sb(dev) 0
     set sb(ino) 0
     array get sb
 }
@@ -172,7 +165,6 @@ proc vfs::mk4::stat {db name} {
 proc vfs::mk4::access {db name mode} {
     #::vfs::log "mk4-access $name $mode"
     # This needs implementing better.  
-    #tclLog "mk4vfs::driver $db access $name $mode"
     switch -- $mode {
        0 {
            # exists
@@ -362,6 +354,10 @@ proc mk4vfs::init {db} {
     }
 }
 
+proc mk4vfs::mount {args} {
+    uplevel ::vfs::mk4::Mount $args
+}
+
 proc mk4vfs::_mount {path file args} {
     variable uid
     set db mk4vfs[incr uid]
@@ -388,8 +384,21 @@ proc mk4vfs::_commit {db} {
     mk::file commit $db
 }
 
-proc mk4vfs::umount {db} {
-    tclLog [list unmount $db]
+proc mk4vfs::umount {local} {
+    foreach {db path} [mk::file open] {
+      if {[string equal $local $path]} {
+       uplevel ::vfs::mk4::Unmount $db $local
+       return
+      }
+    }
+    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
 }
 
@@ -470,7 +479,6 @@ proc mk4vfs::stat {db path arr} {
     set sb(type)       $type
     set sb(view)       $view
     set sb(ino)                $cur
-    set sb(dev)                [list mk4vfs::driver $db]
 
     if { [string equal $type "directory"] } {
         set sb(atime)   0
@@ -502,161 +510,6 @@ proc mk4vfs::stat {db path arr} {
     #}
 }
 
-proc mk4vfs::driver {db option args} {
-    #tclLog "mk4vfs::driver $db $option $args"
-    switch -- $option {
-    lstat       {return [uplevel 1 [concat [list mk4vfs::stat $db] $args]]}
-    chdir       {return [lindex $args 0]}
-    access      {
-       # This needs implementing better.  The 'lindex $args 1' is
-       # the access mode we should be checking.
-       set mode [lindex $args 1]
-       #tclLog "mk4vfs::driver $db access [lindex $args 0] $mode"
-       switch -- $mode {
-           0 {
-               # exists
-               if {![catch {stat $db [lindex $args 0] sb}]} {
-                   return
-               }
-           }
-           1 {
-               # executable
-               if {![catch {stat $db [lindex $args 0] sb}]} {
-                   return
-               }
-           }
-           2 {
-               # writable
-               if {![catch {stat $db [lindex $args 0] sb}]} {
-                   return
-               }
-           }
-           4 {
-               # readable
-               if {![catch {stat $db [lindex $args 0] sb}]} {
-                   return
-               }
-           }
-       }
-       #tclLog "access bad"
-       error "bad file" 
-    }
-    removedirectory {
-       return [uplevel 1 [concat [list mk4vfs::delete $db] $args]]
-    }
-    atime       {
-       # Not implemented
-    }
-    mtime       -
-    delete      -
-    stat        -
-    getdir      -
-    mkdir       {return [uplevel 1 [concat [list mk4vfs::$option $db] $args]]}
-    
-    open        {
-            set file [lindex $args 0]
-            set mode [lindex $args 1]
-
-            switch -glob -- $mode {
-            {}  -
-            r   {
-                    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 {stat $db $file sb }] } {
-                        #tclLog "stat failed - creating $file"
-                        # Create file
-                        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 {stat $db $file sb }] } {
-                        #tclLog "stat failed - creating $file"
-                        # Create file
-                        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\""
-                }
-            }
-        }
-    sync        {eval [list mk::file commit $db] [lrange $args 1 end]}
-    umount      {eval [list mk::file close $db] $args}
-    default     {
-            return -code error "bad option \"$option\": must be one of chdir, delete, getdir, load, lstat, mkdir, open, stat, sync, or umount"
-        }
-    }
-}
-
 proc mk4vfs::do_close {fd mode cur} {
     # Set size to -1 before the seek - just in case it fails.
     
@@ -692,6 +545,7 @@ proc mk4vfs::do_close {fd mode cur} {
     } err]} {
        global errorInfo
        tclLog "mk4vfs::do_close callback error: $err $errorInfo"
+###!!! return -code error $err
     }
 }
 
index c1c284c582a0584e58db5796193376b5cb9def71..3ce0455f92c1de1c5c6c3d6d293a731a65ea7f0f 100644 (file)
@@ -18,15 +18,13 @@ namespace eval ::vfs::urltype {}
 
 proc vfs::urltype::Mount {type} {
     set mountPoint [_typeToMount $type]
-    ::vfs::addVolume $mountPoint
-    ::vfs::filesystem mount $mountPoint [list vfs::urltype::handler $type]
+    ::vfs::filesystem mount -volume $mountPoint [list vfs::urltype::handler $type]
     return "Mounted at \"${mountPoint}\""
 }
 
 proc vfs::urltype::Unmount {type} {
     set mountPoint [_typeToMount $type]
-    ::vfs::filesystem unmount $mountPoint
-    ::vfs::removeVolume $mountPoint
+    ::vfs::filesystem unmount -volume $mountPoint
 }
 
 proc vfs::urltype::_typeToMount {type} {
index 48f6963dbfc90db01197f3969b2387b2cfa26632..829df590e24b2bd7aa02e6eb59816eb725e73f0c 100644 (file)
@@ -7,30 +7,6 @@ namespace eval ::vfs {
     if {[info exists env(VFS_DEBUG)]} {
        set debug $env(VFS_DEBUG)
     }
-    variable volumes ""
-}
-
-# This procedure is called by Tcl when we are registered.
-# The results of the procedure, as well as being listed
-# in 'file volumes' affect whether files are treated as
-# relative or absolute as well.
-proc ::vfs::listVolumes {} {
-    variable volumes
-    return $volumes
-}
-
-proc ::vfs::addVolume {vol} {
-    variable volumes
-    lappend volumes $vol
-}
-
-proc ::vfs::removeVolume {vol} {
-    variable volumes
-    set idx [lsearch -exact $volumes $vol]
-    if {$idx == -1} {
-       return -code error "No such volume \"$vol\""
-    }
-    set volumes [lreplace $volumes $idx $idx]
 }
 
 proc ::vfs::autoMountExtension {ext cmd {pkg ""}} {
index 964f190c035a9116807144d507057e22e9c0c811..7564a405dd4f34f5e0b312fcea833a625468ba6a 100644 (file)
@@ -13,7 +13,7 @@ VFS_VERSION = 1.0
 DLL_VERSION = 10
 
 # comment the following line to compile with symbols
-NODEBUG=1
+NODEBUG=0
 
 !IF "$(NODEBUG)" == "1"
 DEBUGDEFINES =