-// 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
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
-// +build windows\r
+// +build windows
-// Package fsnotify allows the user to receive\r
-// file system event notifications on Windows.\r
+// Package fsnotify allows the user to receive
+// file system event notifications on Windows.
package fsnotify
import (
"unsafe"
)
-// Event is the type of the notification messages\r
-// received on the watcher's Event channel.\r
+// Event is the type of the notification messages
+// received on the watcher's Event channel.
type FileEvent struct {
- mask uint32 // Mask of events\r
- cookie uint32 // Unique cookie associating related events (for rename)\r
- Name string // File name (optional)\r
+ mask uint32 // Mask of events
+ cookie uint32 // Unique cookie associating related events (for rename)
+ Name string // File name (optional)
}
-// IsCreate reports whether the FileEvent was triggerd by a creation\r
+// IsCreate reports whether the FileEvent was triggerd by a creation
func (e *FileEvent) IsCreate() bool { return (e.mask & FS_CREATE) == FS_CREATE }
-// IsDelete reports whether the FileEvent was triggerd by a delete\r
+// IsDelete reports whether the FileEvent was triggerd by a delete
func (e *FileEvent) IsDelete() bool {
return ((e.mask&FS_DELETE) == FS_DELETE || (e.mask&FS_DELETE_SELF) == FS_DELETE_SELF)
}
-// IsModify reports whether the FileEvent was triggerd by a file modification or attribute change\r
+// IsModify reports whether the FileEvent was triggerd by a file modification or attribute change
func (e *FileEvent) IsModify() bool {
return ((e.mask&FS_MODIFY) == FS_MODIFY || (e.mask&FS_ATTRIB) == FS_ATTRIB)
}
-// IsRename reports whether the FileEvent was triggerd by a change name\r
+// IsRename reports whether the FileEvent was triggerd by a change name
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)
}
type watch struct {
ov syscall.Overlapped
- 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
+ ino *inode // i-number
+ path string // Directory path
+ mask uint64 // Directory itself is being watched with these notify flags
+ names map[string]uint64 // Map of names being watched and their notify flags
+ rename string // Remembers the old name while renaming a file
buf [4096]byte
}
type indexMap map[uint64]*watch
type watchMap map[uint32]indexMap
-// A Watcher waits for and receives event notifications\r
-// for a specific set of files and directories.\r
+// A Watcher waits for and receives event notifications
+// for a specific set of files and directories.
type Watcher struct {
- port syscall.Handle // Handle to completion port\r
- watches watchMap // Map of watches (key: i-number)\r
- fsnFlags map[string]uint32 // Map of watched files to flags used for filter\r
- input chan *input // Inputs to the reader are sent on this channel\r
- internalEvent chan *FileEvent // Events are queued 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
+ port syscall.Handle // Handle to completion port
+ watches watchMap // Map of watches (key: i-number)
+ fsnFlags map[string]uint32 // Map of watched files to flags used for filter
+ input chan *input // Inputs to the reader are sent on this channel
+ internalEvent chan *FileEvent // Events are queued on this channel
+ Event chan *FileEvent // Events are returned on this channel
+ Error chan error // Errors are sent on this channel
+ isClosed bool // Set to true when Close() is first called
quit chan chan<- error
cookie uint32
}
-// NewWatcher creates and returns a Watcher.\r
+// NewWatcher creates and returns a Watcher.
func NewWatcher() (*Watcher, error) {
port, e := syscall.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 0)
if e != nil {
return w, nil
}
-// 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
+// Close closes a Watcher.
+// It sends a message to the reader goroutine to quit and removes all watches
+// associated with the watcher.
func (w *Watcher) Close() error {
if w.isClosed {
return nil
}
w.isClosed = true
- // Send "quit" message to the reader goroutine\r
+ // Send "quit" message to the reader goroutine
ch := make(chan error)
w.quit <- ch
if err := w.wakeupReader(); err != nil {
return <-ch
}
-// AddWatch adds path to the watched file set.\r
+// AddWatch adds path to the watched file set.
func (w *Watcher) AddWatch(path string, flags uint32) error {
if w.isClosed {
return errors.New("watcher already closed")
return <-in.reply
}
-// Watch adds path to the watched file set, watching all events.\r
+// Watch adds path to the watched file set, watching all events.
func (w *Watcher) watch(path string) error {
return w.AddWatch(path, FS_ALL_EVENTS)
}
-// RemoveWatch removes path from the watched file set.\r
+// RemoveWatch removes path from the watched file set.
func (w *Watcher) removeWatch(path string) error {
in := &input{
op: opRemoveWatch,
return ino, nil
}
-// Must run within the I/O thread.\r
+// Must run within the I/O thread.
func (m watchMap) get(ino *inode) *watch {
if i := m[ino.volume]; i != nil {
return i[ino.index]
return nil
}
-// Must run within the I/O thread.\r
+// Must run within the I/O thread.
func (m watchMap) set(ino *inode, watch *watch) {
i := m[ino.volume]
if i == nil {
i[ino.index] = watch
}
-// Must run within the I/O thread.\r
+// Must run within the I/O thread.
func (w *Watcher) addWatch(pathname string, flags uint64) error {
dir, err := getDir(pathname)
if err != nil {
return nil
}
-// Must run within the I/O thread.\r
+// Must run within the I/O thread.
func (w *Watcher) remWatch(pathname string) error {
dir, err := getDir(pathname)
if err != nil {
watch.mask = 0
} else {
name := filepath.Base(pathname)
- w.sendEvent(watch.path+"/"+name, watch.names[name]&FS_IGNORED)
+ w.sendEvent(watch.path+"\\"+name, watch.names[name]&FS_IGNORED)
delete(watch.names, name)
}
return w.startRead(watch)
}
-// Must run within the I/O thread.\r
+// Must run within the I/O thread.
func (w *Watcher) deleteWatch(watch *watch) {
for name, mask := range watch.names {
if mask&provisional == 0 {
- w.sendEvent(watch.path+"/"+name, mask&FS_IGNORED)
+ w.sendEvent(watch.path+"\\"+name, mask&FS_IGNORED)
}
delete(watch.names, name)
}
}
}
-// Must run within the I/O thread.\r
+// Must run within the I/O thread.
func (w *Watcher) startRead(watch *watch) error {
if e := syscall.CancelIo(watch.ino.handle); e != nil {
w.Error <- os.NewSyscallError("CancelIo", e)
if e != nil {
err := os.NewSyscallError("ReadDirectoryChanges", e)
if e == syscall.ERROR_ACCESS_DENIED && watch.mask&provisional == 0 {
- // Watched directory was probably removed\r
+ // Watched directory was probably removed
if w.sendEvent(watch.path, watch.mask&FS_DELETE_SELF) {
if watch.mask&FS_ONESHOT != 0 {
watch.mask = 0
return nil
}
-// 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
+// readEvents reads from the I/O completion port, converts the
+// received events into Event objects and sends them via the Event channel.
+// Entry point to the I/O thread.
func (w *Watcher) readEvents() {
var (
n, key uint32
switch e {
case syscall.ERROR_ACCESS_DENIED:
- // Watched directory was probably removed\r
+ // Watched directory was probably removed
w.sendEvent(watch.path, watch.mask&FS_DELETE_SELF)
w.deleteWatch(watch)
w.startRead(watch)
continue
case syscall.ERROR_OPERATION_ABORTED:
- // CancelIo was called on this handle\r
+ // CancelIo was called on this handle
continue
default:
w.Error <- os.NewSyscallError("GetQueuedCompletionPort", e)
break
}
- // Point "raw" to the event in the buffer\r
+ // Point "raw" to the event in the buffer
raw := (*syscall.FileNotifyInformation)(unsafe.Pointer(&watch.buf[offset]))
buf := (*[syscall.MAX_PATH]uint16)(unsafe.Pointer(&raw.FileName))
name := syscall.UTF16ToString(buf[:raw.FileNameLength/2])
- fullname := watch.path + "/" + name
+ fullname := watch.path + "\\" + name
var mask uint64
switch raw.Action {
}
}
if raw.Action == syscall.FILE_ACTION_RENAMED_NEW_NAME {
- fullname = watch.path + "/" + watch.rename
+ fullname = watch.path + "\\" + watch.rename
sendNameEvent()
}
- // Move to the next event in the buffer\r
+ // Move to the next event in the buffer
if raw.NextEntryOffset == 0 {
break
}
}
const (
- // Options for AddWatch\r
+ // Options for AddWatch
FS_ONESHOT = 0x80000000
FS_ONLYDIR = 0x1000000
- // Events\r
+ // Events
FS_ACCESS = 0x1
FS_ALL_EVENTS = 0xfff
FS_ATTRIB = 0x4
FS_MOVED_TO = 0x80
FS_MOVE_SELF = 0x800
- // Special events\r
+ // Special events
FS_IGNORED = 0x8000
FS_Q_OVERFLOW = 0x4000
)