vfs(3tcl) | Tcl-only Virtual File Systems | vfs(3tcl) |
::vfs - Commands and Procedures to create virtual filesystems
package require Tcl 8.4
package require vfs ?1.4?
vfs::filesystem info
vfs::filesystem mount
vfs::filesystem unmount
vfs::accessMode mode
vfs::matchDirectories types
vfs::matchFiles types
vfs::matchCorrectTypes types filelist ?inDir?
The ::vfs package provides commands to query, mount and unmount virtual filesystems, and provides as Tcl libraries some facilities for helping the writing of new virtual filesystems in Tcl. Once a virtual filesystem is in place, the standard Tcl file, glob, cd, pwd, open commands, including all their C APIs in the Tcl library (e.g. Tcl_FSOpenFileChannel, Tcl_FSMatchInDirectory,...), can be used within the filesystem (and indeed, properly written extensions such as Tk which may open or read files will also transparently access the virtual filesystem). Because all of Tcl's FS activity passes through a single layer, it can all be intercepted. This package does just that. Notice that this is quite different to overloading the file command in Tcl. We are actually providing vfs replacements for C commands like access, stat. By implementing just a handful of commands at this low level, we ensure that all commands at higher levels function irrespective of what is going on inside the FS layer.
Tcl's filesystem hooks operate on a per-process basis. This means every Tcl interpreter in the same process/application sees the same filesystem, including any virtual filesystems.
The package require vfs command should be used to access this library. It automatically registers the vfs hooks into Tcl's filesystem, and these will not be removed until Tcl exits (if desired, 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. Note that package require vfs has two effects. First of all, when it is issued in any Tcl interpreter it will ensure the vfs hooks have been registered with Tcl's core just once (and if any of those interpreters are later deleted, the vfs hooks will still remain registered - they remain until Tcl exits). The second effect is to provide the command vfs::filesystem which allows the interpreter to intercept filesystem commands and handle them with Tcl code in that interpreter.
There are three somewhat unsupported subcommands of vfs::filesystem, fullynormalize path, posixerror int, internalerror ?script?, which are used to normalize a path (including any final symlink), to register a posix error code with a Tcl error, and to trap/report internal errors in tclvfs implementations respectively.
The vfs package will intercept every filesystem operation which falls within a given mount point, and pass the operation on to the mount point's command in the interpreter which registered it. In general this occurs by the C equivalent of an evaluation like this: eval $command [list $subcmd $root $relative $actualpath] $args.
Here subcmd may be any of the following: access, createdirectory, deletefile, fileattributes, matchindirectory, open, removedirectory, stat, utime. If command takes appropriate action for each of these cases, a complete, perfect virtual filesystem will be achieved, indistinguishable to Tcl from the native filesystem. (CAVEATS: right now I don't expose to Tcl all the permission-related flags of 'glob').
The remaining arguments specify a file path on which to operate (all commands operate on one of these), and any additional arguments which may be required to carry out the action. The file path is specified by three arguments: root is the part of the path which lies outside this filesystem's mount point, relative is the part of the path which lies inside this filesytem, and actualpath is the original (unnormalized) name of the path which was used in the current command wherever it originated (in Tcl or C). For example, if C:/foo/bar/mount.zip/xxx/yyy is a path in your filesystem, where mount.zip is a zip archive which has been mounted (on top of itself) and contains xxx/yyy, and the current working directory is inside xxx, and we evaluate a command like file exists yyy, then root will be C:/foo/bar/mount.zip, relative will be xxx/yyy, and actualpath will be yyy. The file separator between the root and relative is omitted.
Note that most filesystem operations will only require the relative argument to work correctly, but the other arguments are actually required for correct operation of some subcommands.
Almost all of these commands should either return correctly (i.e. with a TCL_OK result at the C level) or they should use vfs::filesystem posixerror to signal the appropriate posix error code. If a Tcl error is thrown, that should be considered a bug, but it will be interpreted as an unknown posix error in the filesystem call. The exceptions to these rules are those filesystem commands which are able to specify a Tcl error message directly: open (when an interpreter is given), matchindirectory and fileattributes (for a set or get operation only). These three commands are allowed to throw any Tcl error message which will be passed along to the caller, or they may throw a posix error which will be handled appropriately.
The actual commands are as follows (where r-r-a represents the standard argument triplet of root, relative and actualpath):
The vfslib provides a number of Tcl procedures which can help with writing command procedures to handle the above possibilities. These are:
Use something like this to debug problems in your implementation: vfs::filesystem internalerror report ; proc report {} { puts stderr $::errorInfo }
There are very few limitations to the vfs code. One subtlety that you may encounter is if you mount a case-sensitive virtual filesystem into a case-insensitive system (e.g. the standard Windows or MacOS fs) and your code relies on case-insensitivity, then it will not run properly in the virtual filesystem. Of course if your code relies on case-insensitivity, it wouldn't run under Tcl on Unix either, so the best solution is to fix your code!
We may add link and lstat commands in the future to allow virtual filesystems to support reading and writing links - this is supported by the C API, but has simply not been exposed to Tcl in this extension, yet.
The Tcl 'Tcl_FSMatchInDirectory' function takes a variety of type information in a Tcl_GlobTypeData structure. We currently only expose the 'type' field from that structure (so the 'permissions' and MacOS type/creator fields are ignored).
vfs, filesystem, file
1.4 | Vfs |