ZVFS: The ZIP Virtual File System Tcl Extension

This page was last modified on 2000/10/24 13:44:25 GMT

Introduction

ZVFS is a Tcl extension that causes Tcl to view the contents of a ZIP archive as real, uncompressed, individually-accessible files. Using ZVFS, you "mount" a ZIP archive on a directory of your filesystem. Thereafter, all of the contents of the ZIP archive appear to be files contained within the directory on which the ZIP file is mounted.

For example, suppose you have a ZIP archive named example1.zip and suppose this archive contains three files named abc.tcl, pqrs.gif, and xyz.tcl. You can mount this ZIP archive as follows:

zvfs::mount  example1.zip   /zip1

After executing the above command, the contents of the ZIP archive appear to be files in the /zip1 directory. So, for instance, you can now execute commands like these:

source /zip1/abc.tcl
image create photo img1 -data /zip1/pqrs.gif
puts "The size of file xyz.tcl is [file size /zip1/xyz.tcl]"

The files /zip1/abc.tcl, /zip1/pqrs.gif, and /zip1.xyz.tcl never really exist as separate files on your disk drive. They are always contained within the ZIP archive and are not unpacked. The ZVFS extension intercepts Tcl's attempt to open and read these files and substitutes data from the ZIP archive that is extracted and decompressed on the fly.

Using ZVFS

Depending on how the ZVFS extension is installed, you might have to first load it before using it. One of the following commands such suffice:

package require zvfs
load ./zvfs.so
load ./zvfs.dll

It could be the case that ZVFS is compiled into your application and does not need loading. If that is so then none of the above commands is required. Otherwise, you will need to use a command like one of the above to get ZVFS linked into your executable.

Once ZVFS is loaded, you have access to the following new Tcl commands:

As discussed above, the zvfs::mount command mounts a new ZIP archive file so that the contents of the archive appear to Tcl to be regular files. The first argument is the name of the ZIP archive file. The second argument is the name of the directory that will appear to hold the contents of the ZIP archive. The ZIP archive may be unmounted using the zvfs::unmount command.

The zvfs::exists checks to see if the file named as its first argument exists in a mounted ZIP archive. You can do almost the same thing with the built-in file exists command of Tcl. The file exists command will return true if the named file is contained in a mounted ZIP archive. But file exists will also return true if its argument is a real file on the disk, whereas zvfs::exists will only return true if the argument is contain in a mounted ZIP archive.

The zvfs::info command takes a single argument which is the name of a file contained in a mounted ZIP archive. If the argument is something other than such a file, this routine returns an empty string. IF the argument is a file in a ZIP archive, then this routine returns the following information about that file:

The zvfs::list command returns a list of all files contained within all mounted ZIP archives. If a single argument is given, that argument is interpreted as a glob pattern and only files that match that glob pattern will match. If the -regexp switch appears then the argument is interpreted as a regular expression and only files that match the regular expression are listed.

The zvfs::filelcopy command may be used to copy a file from one location to another. ZVFS works by putting hooks into the Tcl I/O subsystem. It turns out that the built-in file copy command of Tcl bypasses the Tcl I/O subsystem for efficiency reasons. Hence, file copy will not work on ZVFS files. The zvfs::filecopy command is provided as an alternative.

Limitations

The files in a ZIP archive are read-only. You cannot open a ZVFS mounted file for writing.

ZVFS is implemented using the (undocumented) TclInsertProc() API of the Tcl core. This API allows Tcl extensions to intercept calls to check the status of files or to open files. But there is no provision for handling directory listings, so the glob command will not see ZVFS mounted files. Nor is there any way to intercept attempts to rename or delete files, so those operations are also not supported by ZVFS. Built-in commands or extensions that bypass the Tcl I/O mechanism (ex: the file copy command and the "winico" extension) will not see ZVFS files.

You can source scripts that are ZVFS mounted files. But you cannot load new Tcl extensions out of ZVFS. If you store a shared library or DLL in a ZIP archive, you'll have to first copy the DLL out of the archive into a real disk file before attempting to load it. You can use the built-in zvfs::filecopy command of Tcl to copy the DLL out of ZVFS into a real disk file and then load the copy, if you like. You just can not load the DLL directly out of ZVFS. This is an operating system limitation.

Overlays

ZVFS allows you to mount a ZIP archive on top of an existing filesystem. Tcl first looks for the file in the ZIP archive and if it is not found there it then looks in the underlying filesystem. You can also mount multiple ZIP archives on top of one another. The ZIP archives are searched from the most recently mounted back to the least recently mounted.

This overlay behavior is useful for distributing patches or updates to a large program. Suppose you have a large application that contains many TCL scripts which you distribute as a single ZIP archive file. You can start up your application using code like the following:

foreach file [lsort -dictionary [glob appcode*.zip]] {
  zvfs::mount $file /appcode
}

This loop finds all ZIP archive (in a certain directory) that begin with the prefix appcode. It then mounts each ZIP archive on the same /appcode directory.

You can use this scheme to ship the TCL scripts of your application in a file named appcode000.zip. If there is later a change or update to your program that effects a small subset of the TCL scripts, you can create a patch file named appcode001.zip that contains only the scripts that changed. By placing appcode001.zip in the same directory as appcode000.zip and restarting the application, all the files in appcode001.zip will override files with the same name in appcode000.zip. Subsequent updates can be named appcode002.zip, appcode003, and so forth.

This kind of update scheme makes it very easy to back out a change. Suppose after trying out a particular update, the user decides they do not like it and want to go back to the prior version. All they have to do is remove (or rename) the appropriate appcode*.zip file and restart the application and the code automatically reverts to its previous configuration. Updates are completely and trivially reversible!

Using The Executable As The ZIP Archive

The directory information for most executable formats is at the beginning of the file and the directory information for the ZIP archive format is at the end of the file. This means that you can append extra data to an executable and the operating system will not care and you can prepend information to a ZIP archive and the ZVFS extension will not care. So then, there is nothing to prevent you from appending the ZIP archive to the executable that contains your Tcl interpreter and thereby put your entire application into a single standalone file.

Here is how your proceed. First compile a C program that creates a Tcl interpreter, adds the ZVFS extention, then executes the following lines of TCL code:

zvfs::mount  [info nameofexecutable] /self
source /self/main.tcl

You can execute this TCL code by calling Tcl_Eval() on a small string constant if you like. After you construct this executable, add an empty ZIP archive directory to the end of the file. Then use a ZIP archiver program (ex: pkzip) to add main.tcl and whatever other files you need to the archive.

When you execute the result, the operating system loads and runs the first part of the file as the executable. Then the ZVFS extension kicks in and reads the TCL scripts out of the end of the file.

You can also add the standard Tcl initialization scripts to the ZIP archive at the end of the executable if you want. But a little trickery is required. The problem is that Tcl reads its initialization scripts as it is initializing. Which means that you have to mount the ZIP archive before Tcl is initialized. Which means that you cannot use the zvfs::mount Tcl command to do the mounting.

To read the Tcl initialization scripts from a ZIP archive, you have to mount the ZIP archive using the C API to ZVFS before the Tcl interpreter is initialized. The following C code shows the basic steps:

001  Tcl_FindExecutable(argv[0]);
002  interp = Tcl_CreateInterp();
003  Zvfs_Init(interp);
004  Zvfs_Mount(interp, Tcl_GetNameOfExecutable(), "/zvfs");
005  Tcl_SetVar2(interp, "env", "TCL_LIBRARY", "/zvfs/tcl", TCL_GLOBAL_ONLY);
006  Tcl_Init(interp);

The file that containst the executable (and hence the ZIP archive) is located on line 001. We create the TCL interpreter on line 002. Line 003 initializes the ZVFS extension and line 004 mounts the executable as a ZIP archive at the location /zvfs. Line 005 sets the Tcl variable env(TCL_LIBRARY) to /zvfs/tcl. This will cause the Tcl initialization procedure to look in the directory /zvfs/tcl for its initialization scripts. Finally, the Tcl initialization procedure is started on line 006.

For the sake of simplicity, this example has omitted several steps, but is sufficient to show the general concept. For a complete implementation of this idea see the source code and makefile for Tobe.

Compiling ZVFS

TBD...

Downloading The Sources

The sources to the ZVFS extension are contained in a single C file named zvfs.c. A copy of this file is included in the distribution of Tobe. The sources to ZVFS are not available separately at this time.


Back to the Tobe Home Page