Search code examples
ctclvfs

Tcl pathInFilesystemProc get current filesystem


When creating a vfs using the tcl api how do you get the current filesystem in Tcl_Filesystem.pathInFilesystemProc

My code looks something like this:

typedef struct {
    FILE* dbFile;
    /*...*/

} FSBackend;

void createFS(const char* dbFile)
{

    FSBackend* fsback = (FSBackend*)malloc(sizeof(FSBackend));
    initDb(fsback,dbFile);

    Tcl_Filesystem tfs;

    tfs.typeName="Db Fs";
    tfs.structureLength = sizeof(Tcl_Filesystem);
    tfs.version = TCL_FILESYSTEM_VERSION_1;
    tfs.pathInFilesystemProc = inFsProc;

    /*...*/

    Tcl_FSRegister((void*),tfs);
}

int inFsProc(Tcl_Obj* pathPtr,ClientData* cd)
{
    /* How do I get my FSBackend struct here */
    FSBackend* bk = /* ? */

    int len;

    const char* searchPath = Tcl_GetStringFromObj(pathPtr,&len);

    char* foundPath = findFileInDb(searchPath,bk);

    if (foundPath == 0) {
        return -1;
    }

    cd = buildInternalRep(foundPath,bk);
    return TCL_OK;
}

/**
 ...
*/

int main()
{
    createFS("db1.db");
    createFS("db2.db");
}

How do I, in inFsProc get back the struct I passed into Tcl_FSRegister?

The Tcl_FSData function says it can get it but I would then need to get a Tcl_Filesystem pointer


Solution

  • That's a weird one. The clientData handle there is not used to specify a mount point, but rather a separate capability of the filesystem type. Tcl's internal use of Tcl_FSRegister doesn't use it at all. The code which is as close as anything to a canonical use of it is the tclvfs package.

    https://github.com/tcl-mirror/tclvfs/blob/master/generic/vfs.c#L385 shows us the use:

    static void 
    Vfs_RegisterWithInterp(interp)
        Tcl_Interp *interp;
    {
        ClientData vfsAlreadyRegistered;
        /* 
         * We need to know if the interpreter is deleted, so we can
         * remove all interp-specific mounts.
         */
        Tcl_SetAssocData(interp, "vfs::inUse", (Tcl_InterpDeleteProc*) 
                 Vfs_UnregisterWithInterp, (ClientData) 1);
        /* 
         * Perform one-off registering of our filesystem if that
         * has not happened before.
         */
        vfsAlreadyRegistered = Tcl_FSData(&vfsFilesystem);
        if (vfsAlreadyRegistered == NULL) {
            Tcl_FSRegister((ClientData)1, &vfsFilesystem);
            Tcl_CreateExitHandler(VfsExitProc, (ClientData)NULL);
            Tcl_CreateThreadExitHandler(VfsThreadExitProc, NULL);
        }
    }
    

    As you can see, the clientData there is really just being used as a marker so the code knows whether to do one-time initialisation.

    To discover what the mount mapping is, you'll need to keep internal structures. You're strongly recommended to make the Tcl_Filesystem structure instance itself be global (or rather static at file scope) in your code.