Index: example/main.tcl ================================================================== --- example/main.tcl +++ example/main.tcl @@ -1,9 +1,49 @@ -set fd [open "//xvfs:/example/foo"] +set file "//xvfs:/example/foo" + +set fd [open $file] seek $fd 0 end seek $fd -1 current set check [read $fd 1] if {$check != "\n"} { error "EXPECTED: (new line); GOT: [binary encode hex $check]" } +close $fd + +set fd1 [open $file] +set fd2 [open $file] +set data1 [read $fd1] +close $fd1 +set data2 [read $fd2] +close $fd2 +if {$data1 != $data2} { + error "EXPECTED match, differs" +} + +set fd [open $file] +seek $fd 0 end +set size [tell $fd] +close $fd +set fd [open $file] +set done false +set calls 0 +set output "" +fileevent $fd readable [list apply {{fd} { + set pos [tell $fd] + set x [read $fd 1] + if {[string length $x] == 0} { + set ::done true + fileevent $fd readable "" + } + + lappend ::output $pos + incr ::calls +}} $fd] +vwait done +if {$calls != ($size + 1)} { + error "EXPECTED [expr {$size + 1}], got $calls" +} +if {[lsort -integer $output] != $output} { + error "EXPECTED [lsort -integer $output], GOT $output" +} puts "ALL TESTS PASSED" Index: xvfs-core.c ================================================================== --- xvfs-core.c +++ xvfs-core.c @@ -107,34 +107,50 @@ Tcl_Channel channel; struct xvfs_tclfs_instance_info *fsInstanceInfo; Tcl_Obj *path; Tcl_WideInt currentOffset; Tcl_WideInt fileSize; + int eofMarked; +}; +struct xvfs_tclfs_channel_event { + Tcl_Event tcl; + struct xvfs_tclfs_channel_id *channelInstanceData; }; static Tcl_ChannelType xvfs_tclfs_channelType; static Tcl_Channel xvfs_tclfs_openChannel(Tcl_Obj *path, struct xvfs_tclfs_instance_info *instanceInfo) { struct xvfs_tclfs_channel_id *channelInstanceData; Tcl_Channel channel; Tcl_StatBuf fileInfo; + Tcl_Obj *channelName; int statRet; statRet = instanceInfo->fsInfo->getStatProc(Tcl_GetString(path), &fileInfo); if (statRet < 0) { return(NULL); } channelInstanceData = (struct xvfs_tclfs_channel_id *) Tcl_Alloc(sizeof(*channelInstanceData)); channelInstanceData->currentOffset = 0; + channelInstanceData->eofMarked = 0; channelInstanceData->channel = NULL; + + channelName = Tcl_ObjPrintf("xvfs0x%llx", (unsigned long long) channelInstanceData); + if (!channelName) { + Tcl_Free((char *) channelInstanceData); + + return(NULL); + } + Tcl_IncrRefCount(channelName); channelInstanceData->fsInstanceInfo = instanceInfo; - channelInstanceData->path = path; channelInstanceData->fileSize = fileInfo.st_size; + channelInstanceData->path = path; Tcl_IncrRefCount(path); - channel = Tcl_CreateChannel(&xvfs_tclfs_channelType, Tcl_GetString(path), channelInstanceData, TCL_READABLE); + channel = Tcl_CreateChannel(&xvfs_tclfs_channelType, Tcl_GetString(channelName), channelInstanceData, TCL_READABLE); + Tcl_DecrRefCount(channelName); if (!channel) { Tcl_DecrRefCount(path); Tcl_Free((char *) channelInstanceData); return(NULL); @@ -161,10 +177,18 @@ const unsigned char *data; Tcl_WideInt offset, length; char *path; channelInstanceData = (struct xvfs_tclfs_channel_id *) channelInstanceData_p; + + /* + * If we are already at the end of the file we can skip + * attempting to read it + */ + if (channelInstanceData->eofMarked) { + return(0); + } path = Tcl_GetString(channelInstanceData->path); offset = channelInstanceData->currentOffset; length = bufSize; @@ -174,23 +198,58 @@ *errorCodePtr = xvfs_errorToErrno(length); return(-1); } - memcpy(buf, data, length); + if (length == 0) { + channelInstanceData->eofMarked = 1; + } else { + memcpy(buf, data, length); - channelInstanceData->currentOffset += length; + channelInstanceData->currentOffset += length; + } return(length); } + +static int xvfs_tclfs_watchChannelEvent(Tcl_Event *event_p, int flags) { + struct xvfs_tclfs_channel_id *channelInstanceData; + struct xvfs_tclfs_channel_event *event; + + event = (struct xvfs_tclfs_channel_event *) event_p; + channelInstanceData = event->channelInstanceData; + + Tcl_NotifyChannel(channelInstanceData->channel, TCL_READABLE); + + return(0); +} static void xvfs_tclfs_watchChannel(ClientData channelInstanceData_p, int mask) { + struct xvfs_tclfs_channel_id *channelInstanceData; + struct xvfs_tclfs_channel_event *event; + if ((mask & TCL_READABLE) != TCL_READABLE) { return; } -/* XXX:TODO: fprintf(stderr, "Incomplete watch called, mask = %i\n", mask); */ + channelInstanceData = (struct xvfs_tclfs_channel_id *) channelInstanceData_p; + + /* + * If the read call has marked that we have reached EOF, + * do not signal any further + */ + if (channelInstanceData->eofMarked) { + return; + } + + event = (struct xvfs_tclfs_channel_event *) Tcl_Alloc(sizeof(*event)); + event->tcl.proc = xvfs_tclfs_watchChannelEvent; + event->tcl.nextPtr = NULL; + event->channelInstanceData = channelInstanceData; + + Tcl_QueueEvent((Tcl_Event *) event, TCL_QUEUE_TAIL); + return; } static int xvfs_tclfs_seekChannel(ClientData channelInstanceData_p, long offset, int mode, int *errorCodePtr) { struct xvfs_tclfs_channel_id *channelInstanceData; @@ -226,11 +285,14 @@ *errorCodePtr = xvfs_errorToErrno(XVFS_RV_ERR_EINVAL); return(-1); } - channelInstanceData->currentOffset = newOffset; + if (newOffset != channelInstanceData->currentOffset) { + channelInstanceData->eofMarked = 0; + channelInstanceData->currentOffset = newOffset; + } return(channelInstanceData->currentOffset); } static void xvfs_tclfs_prepareChannelType(void) {