Skip to content

Commit

Permalink
BSD - FSNotify flags
Browse files Browse the repository at this point in the history
  • Loading branch information
howeyc committed Jul 2, 2012
1 parent 0ed1e1e commit 97df35b
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 22 deletions.
54 changes: 54 additions & 0 deletions fsnotify.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,60 @@ package fsnotify

import "fmt"

const (
FSN_CREATE = 1
FSN_MODIFY = 2
FSN_DELETE = 4
FSN_RENAME = 8

FSN_ALL = FSN_MODIFY | FSN_DELETE | FSN_RENAME | FSN_CREATE
)

// Purge events from interal chan to external chan if passes filter
func (w *Watcher) purgeEvents() {
for ev := range w.internalEvent {
sendEvent := false
fsnFlags := w.fsnFlags[ev.Name]

if (fsnFlags&FSN_CREATE == FSN_CREATE) && ev.IsCreate() {
sendEvent = true
}

if (fsnFlags&FSN_MODIFY == FSN_MODIFY) && ev.IsModify() {
sendEvent = true
}

if (fsnFlags&FSN_DELETE == FSN_DELETE) && ev.IsDelete() {
sendEvent = true
}

if (fsnFlags&FSN_RENAME == FSN_RENAME) && ev.IsRename() {
sendEvent = true
}

if sendEvent {
w.Event <- ev
}
}

close(w.Event)
}

func (w *Watcher) Watch(path string) error {
w.fsnFlags[path] = FSN_ALL
return w.watch(path)
}

func (w *Watcher) WatchFlags(path string, flags uint32) error {
w.fsnFlags[path] = flags
return w.watch(path)
}

func (w *Watcher) RemoveWatch(path string) error {
delete(w.fsnFlags, path)
return w.removeWatch(path)
}

// String formats the event e in the form
// "filename: DELETE|MODIFY|..."
func (e *FileEvent) String() string {
Expand Down
51 changes: 29 additions & 22 deletions fsnotify_bsd.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,17 @@ func (e *FileEvent) IsModify() bool {
func (e *FileEvent) IsRename() bool { return (e.mask & NOTE_RENAME) == NOTE_RENAME }

type Watcher struct {
kq int // File descriptor (as returned by the kqueue() syscall)
watches map[string]int // Map of watched file diescriptors (key: path)
paths map[int]string // Map of watched paths (key: watch descriptor)
finfo map[int]os.FileInfo // Map of file information (isDir, isReg; key: watch descriptor)
Error chan error // Errors are sent on this channel
Event chan *FileEvent // Events are returned on this channel
done chan bool // Channel for sending a "quit message" to the reader goroutine
isClosed bool // Set to true when Close() is first called
kbuf [1]syscall.Kevent_t // An event buffer for Add/Remove watch
kq int // File descriptor (as returned by the kqueue() syscall)
watches map[string]int // Map of watched file diescriptors (key: path)
fsnFlags map[string]uint32 // Map of watched files to flags used for filter
paths map[int]string // Map of watched paths (key: watch descriptor)
finfo map[int]os.FileInfo // Map of file information (isDir, isReg; key: watch descriptor)
Error chan error // Errors are sent on this channel
internalEvent chan *FileEvent // Events are queued on this channel
Event chan *FileEvent // Events are returned on this channel
done chan bool // Channel for sending a "quit message" to the reader goroutine
isClosed bool // Set to true when Close() is first called
kbuf [1]syscall.Kevent_t // An event buffer for Add/Remove watch
}

// NewWatcher creates and returns a new kevent instance using kqueue(2)
Expand All @@ -55,16 +57,19 @@ func NewWatcher() (*Watcher, error) {
return nil, os.NewSyscallError("kqueue", errno)
}
w := &Watcher{
kq: fd,
watches: make(map[string]int),
paths: make(map[int]string),
finfo: make(map[int]os.FileInfo),
Event: make(chan *FileEvent),
Error: make(chan error),
done: make(chan bool, 1),
kq: fd,
watches: make(map[string]int),
fsnFlags: make(map[string]uint32),
paths: make(map[int]string),
finfo: make(map[int]os.FileInfo),
internalEvent: make(chan *FileEvent),
Event: make(chan *FileEvent),
Error: make(chan error),
done: make(chan bool, 1),
}

go w.readEvents()
go w.purgeEvents()
return w, nil
}

Expand All @@ -80,7 +85,7 @@ func (w *Watcher) Close() error {
// Send "quit" message to the reader goroutine
w.done <- true
for path := range w.watches {
w.RemoveWatch(path)
w.removeWatch(path)
}

return nil
Expand Down Expand Up @@ -151,12 +156,12 @@ func (w *Watcher) addWatch(path string, flags uint32) error {
}

// Watch adds path to the watched file set, watching all events.
func (w *Watcher) Watch(path string) error {
func (w *Watcher) watch(path string) error {
return w.addWatch(path, NOTE_ALLEVENTS)
}

// RemoveWatch removes path from the watched file set.
func (w *Watcher) RemoveWatch(path string) error {
func (w *Watcher) removeWatch(path string) error {
watchfd, ok := w.watches[path]
if !ok {
return errors.New(fmt.Sprintf("can't remove non-existent kevent watch for: %s", path))
Expand Down Expand Up @@ -202,7 +207,7 @@ func (w *Watcher) readEvents() {
if errno != nil {
w.Error <- os.NewSyscallError("close", errno)
}
close(w.Event)
close(w.internalEvent)
close(w.Error)
return
}
Expand Down Expand Up @@ -236,7 +241,7 @@ func (w *Watcher) readEvents() {
w.sendDirectoryChangeEvents(fileEvent.Name)
} else {
// Send the event on the events channel
w.Event <- fileEvent
w.internalEvent <- fileEvent
}

// Move to next event
Expand All @@ -258,6 +263,7 @@ func (w *Watcher) watchDirectoryFiles(dirPath string) error {
filePath := filepath.Join(dirPath, fileInfo.Name())
// Watch file to mimic linux fsnotify
e := w.addWatch(filePath, NOTE_DELETE|NOTE_WRITE|NOTE_RENAME)
w.fsnFlags[filePath] = FSN_ALL
if e != nil {
return e
}
Expand All @@ -283,11 +289,12 @@ func (w *Watcher) sendDirectoryChangeEvents(dirPath string) {
if fileInfo.IsDir() == false {
filePath := filepath.Join(dirPath, fileInfo.Name())
if w.watches[filePath] == 0 {
w.fsnFlags[filePath] = FSN_ALL
// Send create event
fileEvent := new(FileEvent)
fileEvent.Name = filePath
fileEvent.create = true
w.Event <- fileEvent
w.internalEvent <- fileEvent
}
}
}
Expand Down

0 comments on commit 97df35b

Please sign in to comment.