Index: lib/xvfs/xvfs.tcl ================================================================== --- lib/xvfs/xvfs.tcl +++ lib/xvfs/xvfs.tcl @@ -24,10 +24,31 @@ set char }] ""] return $output } + +proc ::xvfs::sanitizeCStringList {list {prefix ""} {width 80}} { + set lines [list] + set row [list] + foreach item $list { + lappend row "\"[sanitizeCString $item]\"" + + set rowString [join $row {, }] + set rowString "${prefix}${rowString}" + if {[string length $rowString] > $width} { + set row [list] + lappend lines $rowString + unset rowString + } + } + if {[info exists rowString]} { + lappend lines $rowString + } + + return [join $lines "\n"] +} proc ::xvfs::binaryToCHex {binary {prefix ""} {width 10}} { binary scan $binary H* binary set output [list] @@ -69,13 +90,21 @@ set size [string length $data] set data [string trimleft [binaryToCHex $data "\t\t\t"]] close $fd } "directory" { - set data "NULL" set type "XVFS_FILE_TYPE_DIR" - set size "0" + set children $fileInfo(children) + set size [llength $children] + + if {$size == 0} { + set children "NULL" + } else { + set children [string trimleft [sanitizeCStringList $children "\t\t\t"]] + # This initializes it using a C99 compound literal, C99 is required + set children "(const char *\[\]) \{$children\}" + } } default { return -code error "Unable to process $inputFile, unknown type: $fileInfo(type)" } } @@ -82,11 +111,18 @@ puts "\t\{" puts "\t\t.name = \"[sanitizeCString $outputFile]\"," puts "\t\t.type = $type," puts "\t\t.size = $size," - puts "\t\t.data = (unsigned char *) $data" + switch -exact -- $fileInfo(type) { + "file" { + puts "\t\t.data.fileContents = (const unsigned char *) $data" + } + "directory" { + puts "\t\t.data.dirChildren = $children" + } + } puts "\t\}," } proc ::xvfs::processDirectory {fsName directory {subDirectory ""}} { set subDirectories [list] @@ -103,10 +139,11 @@ if {$isTopLevel} { puts "static struct xvfs_file_data xvfs_${fsName}_data\[\] = \{" } # XXX:TODO: Include hidden files ? + set children [list] foreach file [glob -nocomplain -tails -directory $workingDirectory *] { if {$file in {. ..}} { continue } @@ -118,34 +155,37 @@ file lstat $inputFile fileInfo } if {![info exists fileInfo]} { puts stderr "warning: Unable to access $inputFile, skipping" } + + lappend children [file tail $file] if {$fileInfo(type) eq "directory"} { lappend subDirectories $outputFile + continue } processFile $fsName $inputFile $outputFile [array get fileInfo] lappend outputFiles $outputFile } foreach subDirectory $subDirectories { lappend outputFiles {*}[processDirectory $fsName $directory $subDirectory] } + + set inputFile $directory + set outputFile $outputDirectory + unset -nocomplain fileInfo + file stat $inputFile fileInfo + set fileInfo(children) $children + + processFile $fsName $inputFile $outputFile [array get fileInfo] + lappend outputFiles $outputFile if {$isTopLevel} { puts "\};" - -if {0} { - puts "" - puts "static xvfs_${fsName}_nameToIndex\[\] = \{" - foreach outputFile $outputFiles { - puts "\t\"$outputFile\"," - } - puts "\};" -} } return $outputFiles } @@ -188,12 +228,12 @@ printHelp stderr $errors exit 1 } ## 3. Start processing directory and producing initial output - processDirectory $fsName $rootDirectory + set ::xvfs::outputFiles [processDirectory $fsName $rootDirectory] set ::xvfs::fsName $fsName set ::xvfs::rootDirectory $rootDirectory } package provide xvfs 1 Index: xvfs-core.c ================================================================== --- xvfs-core.c +++ xvfs-core.c @@ -1,7 +1,24 @@ #include #include -int Xvfs_Register(Tcl_Interp *interp, const char *fsName, int protocolVersion, xvfs_proc_getChildren_t getChildrenProc, xvfs_proc_getData_t getData) { +/* + * There are three (3) modes of operation for Xvfs_Register: + * 1. standalone -- We register our own Tcl_Filesystem + * and handle requests under `//xvfs:/` + * 2. client -- A single Tcl_Filesystem is registered for the + * interp to handle requests under `//xvfs:/` which + * then dispatches to the appropriate registered + * handler + * 3. flexible -- Attempts to find a core Xvfs instance for the + * process at runtime, if found do #2, otherwise + * fallback to #1 + * + */ +static int Xvfs_Register_Standalone(Tcl_Interp *interp, const char *fsName, int protocolVersion, xvfs_proc_getChildren_t getChildrenProc, xvfs_proc_getData_t getDataProc) { Tcl_SetResult(interp, "Not implemented", NULL); return(TCL_ERROR); } + +int Xvfs_Register(Tcl_Interp *interp, const char *fsName, int protocolVersion, xvfs_proc_getChildren_t getChildrenProc, xvfs_proc_getData_t getDataProc) { + return(Xvfs_Register_Standalone(interp, fsName, protocolVersion, getChildrenProc, getDataProc)); +} Index: xvfs-core.h ================================================================== --- xvfs-core.h +++ xvfs-core.h @@ -3,11 +3,11 @@ #include #define XVFS_PROTOCOL_VERSION 1 -typedef const char **(*xvfs_proc_getChildren_t)(const char *path, Tcl_WideInt limit); -typedef const unsigned char *(*xvfs_proc_getData_t)(const char *path, Tcl_WideInt start, Tcl_WideInt length); +typedef const char **(*xvfs_proc_getChildren_t)(const char *path, Tcl_WideInt *count); +typedef const unsigned char *(*xvfs_proc_getData_t)(const char *path, Tcl_WideInt start, Tcl_WideInt *length); -int Xvfs_Register(Tcl_Interp *interp, const char *fsName, int protocolVersion, xvfs_proc_getChildren_t getChildrenProc, xvfs_proc_getData_t getData); +int Xvfs_Register(Tcl_Interp *interp, const char *fsName, int protocolVersion, xvfs_proc_getChildren_t getChildrenProc, xvfs_proc_getData_t getDataProc); #endif Index: xvfs.c.rvt ================================================================== --- xvfs.c.rvt +++ xvfs.c.rvt @@ -1,9 +1,13 @@ #include #include +#include #include +#define XVFS_NAME_LOOKUP_ERROR (-1) +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) + typedef enum { XVFS_FILE_TYPE_REG, XVFS_FILE_TYPE_DIR } xvfs_file_type_t; @@ -11,31 +15,132 @@ struct xvfs_file_data { const char *name; xvfs_file_type_t type; xvfs_size_t size; - const unsigned char *data; + union { + const unsigned char *fileContents; + const char **dirChildren; + } data; }; +static long xvfs__nameToIndex(const char *path) { + if (path == NULL) { + return(XVFS_NAME_LOOKUP_ERROR); + } + + + if (strcmp(path, "") == 0) { + return(); + } + + return(XVFS_NAME_LOOKUP_ERROR); +} + +static const char **xvfs__getChildren(const char *path, Tcl_WideInt *count) { + struct xvfs_file_data *fileInfo; + long inode; + + /* + * Validate input parameters + */ + if (count == NULL) { + return(NULL); + } + + /* + * Get the inode from the lookup function + */ + inode = xvfs__nameToIndex(path); + if (inode == XVFS_NAME_LOOKUP_ERROR) { + return(NULL); + } + + fileInfo = &xvfs__data[inode]; -static const char **xvfs__getChildren(const char *path, Tcl_WideInt limit) { - return(NULL); + /* + * Ensure this is a directory + */ + if (fileInfo->type != XVFS_FILE_TYPE_DIR) { + return(NULL); + } + + *count = fileInfo->size; + return(fileInfo->data.dirChildren); } -static const unsigned char *xvfs__getData(const char *path, Tcl_WideInt start, Tcl_WideInt length) { - return(NULL); +static const unsigned char *xvfs__getData(const char *path, Tcl_WideInt start, Tcl_WideInt *length) { + struct xvfs_file_data *fileInfo; + Tcl_WideInt resultLength; + long inode; + + /* + * Validate input parameters + */ + if (start < 0) { + return(NULL); + } + + if (length == NULL) { + return(NULL); + } + + if (*length < 0) { + return(NULL); + } + + /* + * Get the inode from the lookup function + */ + inode = xvfs__nameToIndex(path); + if (inode == XVFS_NAME_LOOKUP_ERROR) { + return(NULL); + } + + fileInfo = &xvfs__data[inode]; + + /* + * Ensure this is a file that can be read + */ + if (fileInfo->type != XVFS_FILE_TYPE_REG) { + return(NULL); + } + + /* + * Validate the length + */ + if (start > fileInfo->size) { + *length = -1; + return(NULL); + } + + if (*length == 0) { + resultLength = fileInfo->size - start; + } else { + resultLength = MIN(fileInfo->size - start, *length); + } + *length = resultLength; + + /* + * Return the data + */ + return(fileInfo->data.fileContents + start); } int Xvfs__Init(Tcl_Interp *interp) { int register_ret; + + /* XXX:TODO: Stubs */ register_ret = Xvfs_Register(interp, "", XVFS_PROTOCOL_VERSION, xvfs__getChildren, xvfs__getData); if (register_ret != TCL_OK) { return(register_ret); } return(TCL_OK); }