]> go.fuhry.dev Git - fsnotify.git/commitdiff
Go 1 Released - Updated
authorChris Howey <chris@howey.me>
Wed, 28 Mar 2012 22:45:27 +0000 (15:45 -0700)
committerChris Howey <chris@howey.me>
Wed, 28 Mar 2012 22:45:27 +0000 (15:45 -0700)
Now uses go tool
Also added windows support by using winfsnotify

Makefile [deleted file]
README [new file with mode: 0644]
fsnotify_bsd.go
fsnotify_linux.go
fsnotify_test.go
fsnotify_windows.go [new file with mode: 0644]

diff --git a/Makefile b/Makefile
deleted file mode 100644 (file)
index 9f3a8b8..0000000
--- a/Makefile
+++ /dev/null
@@ -1,22 +0,0 @@
-include $(GOROOT)/src/Make.inc
-
-TARG=exp/fsnotify
-
-GOFILES=\
-       fsnotify.go
-
-GOFILES_linux=\
-       fsnotify_linux.go\
-
-GOFILES_freebsd=\
-       fsnotify_bsd.go\
-
-GOFILES_openbsd=\
-       fsnotify_bsd.go\
-
-GOFILES_darwin=\
-       fsnotify_bsd.go\
-
-GOFILES+=$(GOFILES_$(GOOS))
-
-include $(GOROOT)/src/Make.pkg
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..b55ac48
--- /dev/null
+++ b/README
@@ -0,0 +1,22 @@
+File system notifications for Go
+
+Example:
+    watcher, err := fsnotify.NewWatcher()
+    if err != nil {
+        log.Fatal(err)
+    }
+    err = watcher.Watch("/tmp")
+    if err != nil {
+        log.Fatal(err)
+    }
+    for {
+        select {
+        case ev := <-watcher.Event:
+            log.Println("event:", ev)
+        case err := <-watcher.Error:
+            log.Println("error:", err)
+        }
+    }
+
+TODO:
+Get Windows to pass same tests as BSD and Linux
index 5d48e167cd8f136e2c61834a0662fea05e3d1bb4..03f24deb65c591a4065e860fbffc8e7018f047ee 100644 (file)
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// +build freebsd openbsd netbsd darwin
+
 /*
 Package fsnotify implements filesystem notification.
 
index 08dbfb41d17803a02aa8e276e566bd5c261b27d5..a4d64069270575d0628f1980e377102d3dd15de9 100644 (file)
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// +build linux
+
 /*
 Package fsnotify implements a wrapper for the Linux inotify system.
 
index 6d26d99923e3e88d2cb596c7957fdbd6849b48ab..797920147fed61f65f6825463e06186697fb0d5d 100644 (file)
@@ -20,6 +20,12 @@ func TestFsnotifyDirOnly(t *testing.T) {
 
        const testDir string = "_test"
 
+       // Create directory to watch
+       if os.Mkdir(testDir, 0777) != nil {
+               t.Fatalf("Failed to create test directory: %s", err)
+       }
+       defer os.RemoveAll(testDir)
+
        // Create a file before watching directory
        // This should NOT add any events to the fsnotify event queue
        {
@@ -125,6 +131,12 @@ func TestFsnotifyRename(t *testing.T) {
 
        const testDir string = "_test"
 
+       // Create directory to watch
+       if os.Mkdir(testDir, 0777) != nil {
+               t.Fatalf("Failed to create test directory: %s", err)
+       }
+       defer os.RemoveAll(testDir)
+
        // Add a watch for testDir
        err = watcher.Watch(testDir)
        if err != nil {
@@ -214,6 +226,12 @@ func TestFsnotifyAttrib(t *testing.T) {
 
        const testDir string = "_test"
 
+       // Create directory to watch
+       if os.Mkdir(testDir, 0777) != nil {
+               t.Fatalf("Failed to create test directory: %s", err)
+       }
+       defer os.RemoveAll(testDir)
+
        // Add a watch for testDir
        err = watcher.Watch(testDir)
        if err != nil {
diff --git a/fsnotify_windows.go b/fsnotify_windows.go
new file mode 100644 (file)
index 0000000..86bc6f4
--- /dev/null
@@ -0,0 +1,568 @@
+// Copyright 2011 The Go Authors. All rights reserved.\r
+// Use of this source code is governed by a BSD-style\r
+// license that can be found in the LICENSE file.\r
+\r
+// +build windows\r
+\r
+// Package fsnotify allows the user to receive\r
+// file system event notifications on Windows.\r
+package fsnotify\r
+\r
+import (\r
+        "errors"\r
+        "fmt"\r
+        "os"\r
+        "path/filepath"\r
+        "runtime"\r
+        "syscall"\r
+        "unsafe"\r
+)\r
+\r
+// Event is the type of the notification messages\r
+// received on the watcher's Event channel.\r
+type FileEvent struct {\r
+        mask   uint32 // Mask of events\r
+        cookie uint32 // Unique cookie associating related events (for rename)\r
+        Name   string // File name (optional)\r
+}\r
+\r
+// IsCreate reports whether the FileEvent was triggerd by a creation\r
+func (e *FileEvent) IsCreate() bool { return (e.mask & FS_CREATE) == FS_CREATE}\r
+\r
+// IsDelete reports whether the FileEvent was triggerd by a delete\r
+func (e *FileEvent) IsDelete() bool { return (e.mask & FS_DELETE) == FS_DELETE }\r
+\r
+// IsModify reports whether the FileEvent was triggerd by a file modification\r
+func (e *FileEvent) IsModify() bool { return (e.mask & FS_MODIFY) == FS_MODIFY }\r
+\r
+// IsAttribute reports whether the FileEvent was triggerd by a change of attributes\r
+func (e *FileEvent) IsAttribute() bool { return (e.mask & FS_ATTRIB) == FS_ATTRIB }\r
+\r
+// IsRename reports whether the FileEvent was triggerd by a change name\r
+func (e *FileEvent) IsRename() bool { return ((e.mask & FS_MOVE) == FS_MOVE || (e.mask & FS_MOVE_SELF) == FS_MOVE_SELF || (e.mask & FS_MOVED_FROM) == FS_MOVED_FROM || (e.mask & FS_MOVED_TO) == FS_MOVED_TO) }\r
+\r
+const (\r
+        opAddWatch = iota\r
+        opRemoveWatch\r
+)\r
+\r
+const (\r
+        provisional uint64 = 1 << (32 + iota)\r
+)\r
+\r
+type input struct {\r
+        op    int\r
+        path  string\r
+        flags uint32\r
+        reply chan error\r
+}\r
+\r
+type inode struct {\r
+        handle syscall.Handle\r
+        volume uint32\r
+        index  uint64\r
+}\r
+\r
+type watch struct {\r
+        ov     syscall.Overlapped\r
+        ino    *inode            // i-number\r
+        path   string            // Directory path\r
+        mask   uint64            // Directory itself is being watched with these notify flags\r
+        names  map[string]uint64 // Map of names being watched and their notify flags\r
+        rename string            // Remembers the old name while renaming a file\r
+        buf    [4096]byte\r
+}\r
+\r
+type indexMap map[uint64]*watch\r
+type watchMap map[uint32]indexMap\r
+\r
+// A Watcher waits for and receives event notifications\r
+// for a specific set of files and directories.\r
+type Watcher struct {\r
+        port     syscall.Handle // Handle to completion port\r
+        watches  watchMap       // Map of watches (key: i-number)\r
+        input    chan *input    // Inputs to the reader are sent on this channel\r
+        Event    chan *FileEvent// Events are returned on this channel\r
+        Error    chan error     // Errors are sent on this channel\r
+        isClosed bool           // Set to true when Close() is first called\r
+        quit     chan chan<- error\r
+        cookie   uint32\r
+}\r
+\r
+// NewWatcher creates and returns a Watcher.\r
+func NewWatcher() (*Watcher, error) {\r
+        port, e := syscall.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 0)\r
+        if e != nil {\r
+                return nil, os.NewSyscallError("CreateIoCompletionPort", e)\r
+        }\r
+        w := &Watcher{\r
+                port:    port,\r
+                watches: make(watchMap),\r
+                input:   make(chan *input, 1),\r
+                Event:   make(chan *FileEvent, 50),\r
+                Error:   make(chan error),\r
+                quit:    make(chan chan<- error, 1),\r
+        }\r
+        go w.readEvents()\r
+        return w, nil\r
+}\r
+\r
+// Close closes a Watcher.\r
+// It sends a message to the reader goroutine to quit and removes all watches\r
+// associated with the watcher.\r
+func (w *Watcher) Close() error {\r
+        if w.isClosed {\r
+                return nil\r
+        }\r
+        w.isClosed = true\r
+\r
+        // Send "quit" message to the reader goroutine\r
+        ch := make(chan error)\r
+        w.quit <- ch\r
+        if err := w.wakeupReader(); err != nil {\r
+                return err\r
+        }\r
+        return <-ch\r
+}\r
+\r
+// AddWatch adds path to the watched file set.\r
+func (w *Watcher) AddWatch(path string, flags uint32) error {\r
+        if w.isClosed {\r
+                return errors.New("watcher already closed")\r
+        }\r
+        in := &input{\r
+                op:    opAddWatch,\r
+                path:  filepath.Clean(path),\r
+                flags: flags,\r
+                reply: make(chan error),\r
+        }\r
+        w.input <- in\r
+        if err := w.wakeupReader(); err != nil {\r
+                return err\r
+        }\r
+        return <-in.reply\r
+}\r
+\r
+// Watch adds path to the watched file set, watching all events.\r
+func (w *Watcher) Watch(path string) error {\r
+        return w.AddWatch(path, FS_ALL_EVENTS)\r
+}\r
+\r
+// RemoveWatch removes path from the watched file set.\r
+func (w *Watcher) RemoveWatch(path string) error {\r
+        in := &input{\r
+                op:    opRemoveWatch,\r
+                path:  filepath.Clean(path),\r
+                reply: make(chan error),\r
+        }\r
+        w.input <- in\r
+        if err := w.wakeupReader(); err != nil {\r
+                return err\r
+        }\r
+        return <-in.reply\r
+}\r
+\r
+func (w *Watcher) wakeupReader() error {\r
+        e := syscall.PostQueuedCompletionStatus(w.port, 0, 0, nil)\r
+        if e != nil {\r
+                return os.NewSyscallError("PostQueuedCompletionStatus", e)\r
+        }\r
+        return nil\r
+}\r
+\r
+func getDir(pathname string) (dir string, err error) {\r
+        attr, e := syscall.GetFileAttributes(syscall.StringToUTF16Ptr(pathname))\r
+        if e != nil {\r
+                return "", os.NewSyscallError("GetFileAttributes", e)\r
+        }\r
+        if attr&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {\r
+                dir = pathname\r
+        } else {\r
+                dir, _ = filepath.Split(pathname)\r
+                dir = filepath.Clean(dir)\r
+        }\r
+        return\r
+}\r
+\r
+func getIno(path string) (ino *inode, err error) {\r
+        h, e := syscall.CreateFile(syscall.StringToUTF16Ptr(path),\r
+                syscall.FILE_LIST_DIRECTORY,\r
+                syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,\r
+                nil, syscall.OPEN_EXISTING,\r
+                syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OVERLAPPED, 0)\r
+        if e != nil {\r
+                return nil, os.NewSyscallError("CreateFile", e)\r
+        }\r
+        var fi syscall.ByHandleFileInformation\r
+        if e = syscall.GetFileInformationByHandle(h, &fi); e != nil {\r
+                syscall.CloseHandle(h)\r
+                return nil, os.NewSyscallError("GetFileInformationByHandle", e)\r
+        }\r
+        ino = &inode{\r
+                handle: h,\r
+                volume: fi.VolumeSerialNumber,\r
+                index:  uint64(fi.FileIndexHigh)<<32 | uint64(fi.FileIndexLow),\r
+        }\r
+        return ino, nil\r
+}\r
+\r
+// Must run within the I/O thread.\r
+func (m watchMap) get(ino *inode) *watch {\r
+        if i := m[ino.volume]; i != nil {\r
+                return i[ino.index]\r
+        }\r
+        return nil\r
+}\r
+\r
+// Must run within the I/O thread.\r
+func (m watchMap) set(ino *inode, watch *watch) {\r
+        i := m[ino.volume]\r
+        if i == nil {\r
+                i = make(indexMap)\r
+                m[ino.volume] = i\r
+        }\r
+        i[ino.index] = watch\r
+}\r
+\r
+// Must run within the I/O thread.\r
+func (w *Watcher) addWatch(pathname string, flags uint64) error {\r
+        dir, err := getDir(pathname)\r
+        if err != nil {\r
+                return err\r
+        }\r
+        if flags&FS_ONLYDIR != 0 && pathname != dir {\r
+                return nil\r
+        }\r
+        ino, err := getIno(dir)\r
+        if err != nil {\r
+                return err\r
+        }\r
+        watchEntry := w.watches.get(ino)\r
+        if watchEntry == nil {\r
+                if _, e := syscall.CreateIoCompletionPort(ino.handle, w.port, 0, 0); e != nil {\r
+                        syscall.CloseHandle(ino.handle)\r
+                        return os.NewSyscallError("CreateIoCompletionPort", e)\r
+                }\r
+                watchEntry = &watch{\r
+                        ino:   ino,\r
+                        path:  dir,\r
+                        names: make(map[string]uint64),\r
+                }\r
+                w.watches.set(ino, watchEntry)\r
+                flags |= provisional\r
+        } else {\r
+                syscall.CloseHandle(ino.handle)\r
+        }\r
+        if pathname == dir {\r
+                watchEntry.mask |= flags\r
+        } else {\r
+                watchEntry.names[filepath.Base(pathname)] |= flags\r
+        }\r
+        if err = w.startRead(watchEntry); err != nil {\r
+                return err\r
+        }\r
+        if pathname == dir {\r
+                watchEntry.mask &= ^provisional\r
+        } else {\r
+                watchEntry.names[filepath.Base(pathname)] &= ^provisional\r
+        }\r
+        return nil\r
+}\r
+\r
+// Must run within the I/O thread.\r
+func (w *Watcher) removeWatch(pathname string) error {\r
+        dir, err := getDir(pathname)\r
+        if err != nil {\r
+                return err\r
+        }\r
+        ino, err := getIno(dir)\r
+        if err != nil {\r
+                return err\r
+        }\r
+        watch := w.watches.get(ino)\r
+        if watch == nil {\r
+                return fmt.Errorf("can't remove non-existent watch for: %s", pathname)\r
+        }\r
+        if pathname == dir {\r
+                w.sendEvent(watch.path, watch.mask&FS_IGNORED)\r
+                watch.mask = 0\r
+        } else {\r
+                name := filepath.Base(pathname)\r
+                w.sendEvent(watch.path+"/"+name, watch.names[name]&FS_IGNORED)\r
+                delete(watch.names, name)\r
+        }\r
+        return w.startRead(watch)\r
+}\r
+\r
+// Must run within the I/O thread.\r
+func (w *Watcher) deleteWatch(watch *watch) {\r
+        for name, mask := range watch.names {\r
+                if mask&provisional == 0 {\r
+                        w.sendEvent(watch.path+"/"+name, mask&FS_IGNORED)\r
+                }\r
+                delete(watch.names, name)\r
+        }\r
+        if watch.mask != 0 {\r
+                if watch.mask&provisional == 0 {\r
+                        w.sendEvent(watch.path, watch.mask&FS_IGNORED)\r
+                }\r
+                watch.mask = 0\r
+        }\r
+}\r
+\r
+// Must run within the I/O thread.\r
+func (w *Watcher) startRead(watch *watch) error {\r
+        if e := syscall.CancelIo(watch.ino.handle); e != nil {\r
+                w.Error <- os.NewSyscallError("CancelIo", e)\r
+                w.deleteWatch(watch)\r
+        }\r
+        mask := toWindowsFlags(watch.mask)\r
+        for _, m := range watch.names {\r
+                mask |= toWindowsFlags(m)\r
+        }\r
+        if mask == 0 {\r
+                if e := syscall.CloseHandle(watch.ino.handle); e != nil {\r
+                        w.Error <- os.NewSyscallError("CloseHandle", e)\r
+                }\r
+                delete(w.watches[watch.ino.volume], watch.ino.index)\r
+                return nil\r
+        }\r
+        e := syscall.ReadDirectoryChanges(watch.ino.handle, &watch.buf[0],\r
+                uint32(unsafe.Sizeof(watch.buf)), false, mask, nil, &watch.ov, 0)\r
+        if e != nil {\r
+                err := os.NewSyscallError("ReadDirectoryChanges", e)\r
+                if e == syscall.ERROR_ACCESS_DENIED && watch.mask&provisional == 0 {\r
+                        // Watched directory was probably removed\r
+                        if w.sendEvent(watch.path, watch.mask&FS_DELETE_SELF) {\r
+                                if watch.mask&FS_ONESHOT != 0 {\r
+                                        watch.mask = 0\r
+                                }\r
+                        }\r
+                        err = nil\r
+                }\r
+                w.deleteWatch(watch)\r
+                w.startRead(watch)\r
+                return err\r
+        }\r
+        return nil\r
+}\r
+\r
+// readEvents reads from the I/O completion port, converts the\r
+// received events into Event objects and sends them via the Event channel.\r
+// Entry point to the I/O thread.\r
+func (w *Watcher) readEvents() {\r
+        var (\r
+                n, key uint32\r
+                ov     *syscall.Overlapped\r
+        )\r
+        runtime.LockOSThread()\r
+\r
+        for {\r
+                e := syscall.GetQueuedCompletionStatus(w.port, &n, &key, &ov, syscall.INFINITE)\r
+                watch := (*watch)(unsafe.Pointer(ov))\r
+\r
+                if watch == nil {\r
+                        select {\r
+                        case ch := <-w.quit:\r
+                                for _, index := range w.watches {\r
+                                        for _, watch := range index {\r
+                                                w.deleteWatch(watch)\r
+                                                w.startRead(watch)\r
+                                        }\r
+                                }\r
+                                var err error\r
+                                if e := syscall.CloseHandle(w.port); e != nil {\r
+                                        err = os.NewSyscallError("CloseHandle", e)\r
+                                }\r
+                                close(w.Event)\r
+                                close(w.Error)\r
+                                ch <- err\r
+                                return\r
+                        case in := <-w.input:\r
+                                switch in.op {\r
+                                case opAddWatch:\r
+                                        in.reply <- w.addWatch(in.path, uint64(in.flags))\r
+                                case opRemoveWatch:\r
+                                        in.reply <- w.removeWatch(in.path)\r
+                                }\r
+                        default:\r
+                        }\r
+                        continue\r
+                }\r
+\r
+                switch e {\r
+                case syscall.ERROR_ACCESS_DENIED:\r
+                        // Watched directory was probably removed\r
+                        w.sendEvent(watch.path, watch.mask&FS_DELETE_SELF)\r
+                        w.deleteWatch(watch)\r
+                        w.startRead(watch)\r
+                        continue\r
+                case syscall.ERROR_OPERATION_ABORTED:\r
+                        // CancelIo was called on this handle\r
+                        continue\r
+                default:\r
+                        w.Error <- os.NewSyscallError("GetQueuedCompletionPort", e)\r
+                        continue\r
+                case nil:\r
+                }\r
+\r
+                var offset uint32\r
+                for {\r
+                        if n == 0 {\r
+                               // WTF is this??\r
+                                //w.Event <- &Event{Mask: FS_Q_OVERFLOW}\r
+                                w.Error <- errors.New("short read in readEvents()")\r
+                                break\r
+                        }\r
+\r
+                        // Point "raw" to the event in the buffer\r
+                        raw := (*syscall.FileNotifyInformation)(unsafe.Pointer(&watch.buf[offset]))\r
+                        buf := (*[syscall.MAX_PATH]uint16)(unsafe.Pointer(&raw.FileName))\r
+                        name := syscall.UTF16ToString(buf[:raw.FileNameLength/2])\r
+                        fullname := watch.path + "/" + name\r
+\r
+                        var mask uint64\r
+                        switch raw.Action {\r
+                        case syscall.FILE_ACTION_REMOVED:\r
+                                mask = FS_DELETE_SELF\r
+                        case syscall.FILE_ACTION_MODIFIED:\r
+                                mask = FS_MODIFY\r
+                        case syscall.FILE_ACTION_RENAMED_OLD_NAME:\r
+                                watch.rename = name\r
+                        case syscall.FILE_ACTION_RENAMED_NEW_NAME:\r
+                                if watch.names[watch.rename] != 0 {\r
+                                        watch.names[name] |= watch.names[watch.rename]\r
+                                        delete(watch.names, watch.rename)\r
+                                        mask = FS_MOVE_SELF\r
+                                }\r
+                        }\r
+\r
+                        sendNameEvent := func() {\r
+                                if w.sendEvent(fullname, watch.names[name]&mask) {\r
+                                        if watch.names[name]&FS_ONESHOT != 0 {\r
+                                                delete(watch.names, name)\r
+                                        }\r
+                                }\r
+                        }\r
+                        if raw.Action != syscall.FILE_ACTION_RENAMED_NEW_NAME {\r
+                                sendNameEvent()\r
+                        }\r
+                        if raw.Action == syscall.FILE_ACTION_REMOVED {\r
+                                w.sendEvent(fullname, watch.names[name]&FS_IGNORED)\r
+                                delete(watch.names, name)\r
+                        }\r
+                        if w.sendEvent(fullname, watch.mask&toFSnotifyFlags(raw.Action)) {\r
+                                if watch.mask&FS_ONESHOT != 0 {\r
+                                        watch.mask = 0\r
+                                }\r
+                        }\r
+                        if raw.Action == syscall.FILE_ACTION_RENAMED_NEW_NAME {\r
+                                fullname = watch.path + "/" + watch.rename\r
+                                sendNameEvent()\r
+                        }\r
+\r
+                        // Move to the next event in the buffer\r
+                        if raw.NextEntryOffset == 0 {\r
+                                break\r
+                        }\r
+                        offset += raw.NextEntryOffset\r
+                }\r
+\r
+                if err := w.startRead(watch); err != nil {\r
+                        w.Error <- err\r
+                }\r
+        }\r
+}\r
+\r
+func (w *Watcher) sendEvent(name string, mask uint64) bool {\r
+        if mask == 0 {\r
+                return false\r
+        }\r
+        event := &FileEvent{mask: uint32(mask), Name: name}\r
+        if mask&FS_MOVE != 0 {\r
+                if mask&FS_MOVED_FROM != 0 {\r
+                        w.cookie++\r
+                }\r
+                event.cookie = w.cookie\r
+        }\r
+        select {\r
+        case ch := <-w.quit:\r
+                w.quit <- ch\r
+        case w.Event <- event:\r
+        }\r
+        return true\r
+}\r
+\r
+func toWindowsFlags(mask uint64) uint32 {\r
+        var m uint32\r
+        if mask&FS_ACCESS != 0 {\r
+                m |= syscall.FILE_NOTIFY_CHANGE_LAST_ACCESS\r
+        }\r
+        if mask&FS_MODIFY != 0 {\r
+                m |= syscall.FILE_NOTIFY_CHANGE_LAST_WRITE\r
+        }\r
+        if mask&FS_ATTRIB != 0 {\r
+                m |= syscall.FILE_NOTIFY_CHANGE_ATTRIBUTES\r
+        }\r
+        if mask&(FS_MOVE|FS_CREATE|FS_DELETE) != 0 {\r
+                m |= syscall.FILE_NOTIFY_CHANGE_FILE_NAME | syscall.FILE_NOTIFY_CHANGE_DIR_NAME\r
+        }\r
+        return m\r
+}\r
+\r
+func toFSnotifyFlags(action uint32) uint64 {\r
+        switch action {\r
+        case syscall.FILE_ACTION_ADDED:\r
+                return FS_CREATE\r
+        case syscall.FILE_ACTION_REMOVED:\r
+                return FS_DELETE\r
+        case syscall.FILE_ACTION_MODIFIED:\r
+                return FS_MODIFY\r
+        case syscall.FILE_ACTION_RENAMED_OLD_NAME:\r
+                return FS_MOVED_FROM\r
+        case syscall.FILE_ACTION_RENAMED_NEW_NAME:\r
+                return FS_MOVED_TO\r
+        }\r
+        return 0\r
+}\r
+\r
+const (\r
+        // Options for AddWatch\r
+        FS_ONESHOT = 0x80000000\r
+        FS_ONLYDIR = 0x1000000\r
+\r
+        // Events\r
+        FS_ACCESS      = 0x1\r
+        FS_ALL_EVENTS  = 0xfff\r
+        FS_ATTRIB      = 0x4\r
+        FS_CLOSE       = 0x18\r
+        FS_CREATE      = 0x100\r
+        FS_DELETE      = 0x200\r
+        FS_DELETE_SELF = 0x400\r
+        FS_MODIFY      = 0x2\r
+        FS_MOVE        = 0xc0\r
+        FS_MOVED_FROM  = 0x40\r
+        FS_MOVED_TO    = 0x80\r
+        FS_MOVE_SELF   = 0x800\r
+\r
+        // Special events\r
+        FS_IGNORED    = 0x8000\r
+        FS_Q_OVERFLOW = 0x4000\r
+)\r
+\r
+var eventBits = []struct {\r
+        Value uint32\r
+        Name  string\r
+}{\r
+        {FS_ACCESS, "FS_ACCESS"},\r
+        {FS_ATTRIB, "FS_ATTRIB"},\r
+        {FS_CREATE, "FS_CREATE"},\r
+        {FS_DELETE, "FS_DELETE"},\r
+        {FS_DELETE_SELF, "FS_DELETE_SELF"},\r
+        {FS_MODIFY, "FS_MODIFY"},\r
+        {FS_MOVED_FROM, "FS_MOVED_FROM"},\r
+        {FS_MOVED_TO, "FS_MOVED_TO"},\r
+        {FS_MOVE_SELF, "FS_MOVE_SELF"},\r
+        {FS_IGNORED, "FS_IGNORED"},\r
+        {FS_Q_OVERFLOW, "FS_Q_OVERFLOW"},\r
+}\r