Version 0 of tailf-inotify

Updated 2008-02-03 17:13:53 by alsterg

This is a more efficient implementation of the tailf functionality based on the tcl-inotify package. It has the advantage that it doesn't suffer from polling overhead as the other implementations. Inotify asynchronously informs tailf of write changes in a file.


#!/bin/tcl # An efficient tailf implementation using inotify lappend auto_path pwd/lib

package require inotify package provide tailf

namespace eval tailf { namespace export add rem

array set File2wd {} array set Wd2info {}

inotify create ::tailf::watch ::tailf::handler

proc getpos {filepath} {

    set fd [open $filepath r]
    seek $fd 0 end
    set pos [tell $fd]
    close $fd
    return $pos

}

proc handler {args} {

    variable File2wd
    variable Wd2info

    set events [watch read]

    foreach {wd flags cookie filepath} $events {
        if {! [info exists Wd2info($wd)]} {
            return
        }

        set diff {}
        set callback [lindex $Wd2info($wd) 1]
        set filepath [lindex $Wd2info($wd) 2]
        set old [lindex $Wd2info($wd) 0]
        set new [getpos $filepath]
        set Wd2info($wd) [lreplace $Wd2info($wd) 0 0 $new]

        if {$new < $old} {
            set old 0
        }

        if {$old != $new} {
            set fd [open $filepath r]
            seek $fd $old
            while {[gets $fd line] >= 0} {
                append diff "$line" "\n"
            }
            close $fd
        }

        eval [list $callback $filepath $diff]
    }
    return

}

proc add {filepath callback} {

    variable File2wd
    variable Wd2info

    if {[file exists $filepath] && [file isfile $filepath]} {
        set pos [getpos $filepath]
        set wd [watch add $filepath {w}]
        set File2wd($filepath) $wd
        set Wd2info($wd) [list $pos $callback $filepath]
    } else {
        error "is either not a file, or does not exist"
    }

}

proc rem {filepath} {

    variable File2wd
    variable Wd2info

    if {! [info exists ::tailf::File($filepath)]} {
        error "$filepath is not monitored, or watch has been removed"
    }

    set wd $File2wd($filepath)
    unset Wd2info($wd)
    unset File2wd($filepath)
    watch del $filepath

}

} ;# namespace end


proc puts_hdlr {filepath diff} {

    puts -nonewline $diff

}

::tailf::add "delme.txt" puts_hdlr

set a {} vwait a