Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
G
Geth-Modification
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
张蕾
Geth-Modification
Commits
ef63e9af
Commit
ef63e9af
authored
Mar 18, 2016
by
Felix Lange
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Godeps: add github.com/rjeczalik/notify
parent
ee1682ff
Changes
37
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
37 changed files
with
4702 additions
and
0 deletions
+4702
-0
Godeps.json
Godeps/Godeps.json
+4
-0
.gitignore
Godeps/_workspace/src/github.com/rjeczalik/notify/.gitignore
+88
-0
.travis.yml
...ps/_workspace/src/github.com/rjeczalik/notify/.travis.yml
+30
-0
AUTHORS
Godeps/_workspace/src/github.com/rjeczalik/notify/AUTHORS
+10
-0
LICENSE
Godeps/_workspace/src/github.com/rjeczalik/notify/LICENSE
+21
-0
README.md
Godeps/_workspace/src/github.com/rjeczalik/notify/README.md
+21
-0
appveyor.yml
...s/_workspace/src/github.com/rjeczalik/notify/appveyor.yml
+24
-0
debug.go
Godeps/_workspace/src/github.com/rjeczalik/notify/debug.go
+11
-0
debug_debug.go
..._workspace/src/github.com/rjeczalik/notify/debug_debug.go
+43
-0
doc.go
Godeps/_workspace/src/github.com/rjeczalik/notify/doc.go
+40
-0
event.go
Godeps/_workspace/src/github.com/rjeczalik/notify/event.go
+143
-0
event_fen.go
...s/_workspace/src/github.com/rjeczalik/notify/event_fen.go
+46
-0
event_fsevents.go
...rkspace/src/github.com/rjeczalik/notify/event_fsevents.go
+71
-0
event_inotify.go
...orkspace/src/github.com/rjeczalik/notify/event_inotify.go
+75
-0
event_kqueue.go
...workspace/src/github.com/rjeczalik/notify/event_kqueue.go
+59
-0
event_readdcw.go
...orkspace/src/github.com/rjeczalik/notify/event_readdcw.go
+108
-0
event_stub.go
.../_workspace/src/github.com/rjeczalik/notify/event_stub.go
+31
-0
event_trigger.go
...orkspace/src/github.com/rjeczalik/notify/event_trigger.go
+22
-0
node.go
Godeps/_workspace/src/github.com/rjeczalik/notify/node.go
+271
-0
notify.go
Godeps/_workspace/src/github.com/rjeczalik/notify/notify.go
+74
-0
tree.go
Godeps/_workspace/src/github.com/rjeczalik/notify/tree.go
+22
-0
tree_nonrecursive.go
...pace/src/github.com/rjeczalik/notify/tree_nonrecursive.go
+292
-0
tree_recursive.go
...rkspace/src/github.com/rjeczalik/notify/tree_recursive.go
+354
-0
util.go
Godeps/_workspace/src/github.com/rjeczalik/notify/util.go
+150
-0
watcher.go
Godeps/_workspace/src/github.com/rjeczalik/notify/watcher.go
+85
-0
watcher_fen.go
..._workspace/src/github.com/rjeczalik/notify/watcher_fen.go
+170
-0
watcher_fen_cgo.go
...kspace/src/github.com/rjeczalik/notify/watcher_fen_cgo.go
+141
-0
watcher_fsevents.go
...space/src/github.com/rjeczalik/notify/watcher_fsevents.go
+319
-0
watcher_fsevents_cgo.go
...e/src/github.com/rjeczalik/notify/watcher_fsevents_cgo.go
+190
-0
watcher_inotify.go
...kspace/src/github.com/rjeczalik/notify/watcher_inotify.go
+396
-0
watcher_kqueue.go
...rkspace/src/github.com/rjeczalik/notify/watcher_kqueue.go
+192
-0
watcher_readdcw.go
...kspace/src/github.com/rjeczalik/notify/watcher_readdcw.go
+574
-0
watcher_stub.go
...workspace/src/github.com/rjeczalik/notify/watcher_stub.go
+23
-0
watcher_trigger.go
...kspace/src/github.com/rjeczalik/notify/watcher_trigger.go
+438
-0
watchpoint.go
.../_workspace/src/github.com/rjeczalik/notify/watchpoint.go
+103
-0
watchpoint_other.go
...space/src/github.com/rjeczalik/notify/watchpoint_other.go
+23
-0
watchpoint_readdcw.go
...ace/src/github.com/rjeczalik/notify/watchpoint_readdcw.go
+38
-0
No files found.
Godeps/Godeps.json
View file @
ef63e9af
...
@@ -119,6 +119,10 @@
...
@@ -119,6 +119,10 @@
"ImportPath"
:
"github.com/rcrowley/go-metrics"
,
"ImportPath"
:
"github.com/rcrowley/go-metrics"
,
"Rev"
:
"51425a2415d21afadfd55cd93432c0bc69e9598d"
"Rev"
:
"51425a2415d21afadfd55cd93432c0bc69e9598d"
},
},
{
"ImportPath"
:
"github.com/rjeczalik/notify"
,
"Rev"
:
"5dd6205716539662f8f14ab513552b41eab69d5d"
},
{
{
"ImportPath"
:
"github.com/robertkrimen/otto"
,
"ImportPath"
:
"github.com/robertkrimen/otto"
,
"Rev"
:
"53221230c215611a90762720c9042ac782ef74ee"
"Rev"
:
"53221230c215611a90762720c9042ac782ef74ee"
...
...
Godeps/_workspace/src/github.com/rjeczalik/notify/.gitignore
0 → 100644
View file @
ef63e9af
# Created by https://www.gitignore.io
### OSX ###
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear on external disk
.Spotlight-V100
.Trashes
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### Windows ###
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msm
*.msp
# Windows shortcuts
*.lnk
### Linux ###
*~
# KDE directory preferences
.directory
### Go ###
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof
### vim ###
[._]*.s[a-w][a-z]
[._]s[a-w][a-z]
*.un~
Session.vim
.netrwhist
*~
Godeps/_workspace/src/github.com/rjeczalik/notify/.travis.yml
0 → 100644
View file @
ef63e9af
language
:
go
go
:
-
1.4.3
-
1.6
os
:
-
linux
-
osx
matrix
:
include
:
-
os
:
osx
go
:
1.6
env
:
-
GOFLAGS="-tags kqueue"
env
:
global
:
-
GOBIN=$HOME/bin
-
PATH=$HOME/bin:$PATH
install
:
-
go get golang.org/x/tools/cmd/vet
-
go get -t -v ./...
script
:
-
go tool vet -all .
-
go install $GOFLAGS ./...
-
go test -v -race $GOFLAGS ./...
Godeps/_workspace/src/github.com/rjeczalik/notify/AUTHORS
0 → 100644
View file @
ef63e9af
# List of individuals who contributed to the Notify package.
#
# The up-to-date list of the authors one may obtain with:
#
# ~ $ git shortlog -es | cut -f2 | rev | uniq -f1 | rev
#
Pawel Blaszczyk <blaszczykpb@gmail.com>
Pawel Knap <pawelknap88@gmail.com>
Rafal Jeczalik <rjeczalik@gmail.com>
Godeps/_workspace/src/github.com/rjeczalik/notify/LICENSE
0 → 100644
View file @
ef63e9af
The MIT License (MIT)
Copyright (c) 2014-2015 The Notify Authors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Godeps/_workspace/src/github.com/rjeczalik/notify/README.md
0 → 100644
View file @
ef63e9af
notify [](https://godoc.org/github.com/rjeczalik/notify) [](https://travis-ci.org/rjeczalik/notify "inotify + FSEvents + kqueue") [](https://ci.appveyor.com/project/rjeczalik/notify-246 "ReadDirectoryChangesW") [](https://coveralls.io/r/rjeczalik/notify?branch=master)
======
Filesystem event notification library on steroids. (under active development)
*Documentation*
[
godoc.org/github.com/rjeczalik/notify
](
https://godoc.org/github.com/rjeczalik/notify
)
*Installation*
```
~ $ go get -u github.com/rjeczalik/notify
```
*Projects using notify*
-
[
github.com/rjeczalik/cmd/notify
](
https://godoc.org/github.com/rjeczalik/cmd/notify
)
-
[
github.com/cortesi/devd
](
https://github.com/cortesi/devd
)
-
[
github.com/cortesi/modd
](
https://github.com/cortesi/modd
)
Godeps/_workspace/src/github.com/rjeczalik/notify/appveyor.yml
0 → 100644
View file @
ef63e9af
version
:
"
{build}"
os
:
Windows Server 2012 R2
clone_folder
:
c:\projects\src\github.com\rjeczalik\notify
environment
:
PATH
:
c:\projects\bin;%PATH%
GOPATH
:
c:\projects
NOTIFY_TIMEOUT
:
5s
install
:
-
go version
-
go get golang.org/x/tools/cmd/vet
-
go get -v -t ./...
build_script
:
-
go tool vet -all .
-
go build ./...
-
go test -v -race ./...
test
:
off
deploy
:
off
Godeps/_workspace/src/github.com/rjeczalik/notify/debug.go
0 → 100644
View file @
ef63e9af
// Copyright (c) 2014-2015 The Notify Authors. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
// +build !debug
package
notify
func
dbgprint
(
...
interface
{})
{}
func
dbgprintf
(
string
,
...
interface
{})
{}
Godeps/_workspace/src/github.com/rjeczalik/notify/debug_debug.go
0 → 100644
View file @
ef63e9af
// Copyright (c) 2014-2015 The Notify Authors. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
// +build debug
package
notify
import
(
"fmt"
"os"
"runtime"
"strings"
)
func
dbgprint
(
v
...
interface
{})
{
fmt
.
Printf
(
"[D] "
)
fmt
.
Print
(
v
...
)
fmt
.
Printf
(
"
\n\n
"
)
}
func
dbgprintf
(
format
string
,
v
...
interface
{})
{
fmt
.
Printf
(
"[D] "
)
fmt
.
Printf
(
format
,
v
...
)
fmt
.
Printf
(
"
\n\n
"
)
}
func
dbgcallstack
(
max
int
)
[]
string
{
pc
,
stack
:=
make
([]
uintptr
,
max
),
make
([]
string
,
0
,
max
)
runtime
.
Callers
(
2
,
pc
)
for
_
,
pc
:=
range
pc
{
if
f
:=
runtime
.
FuncForPC
(
pc
);
f
!=
nil
{
fname
:=
f
.
Name
()
idx
:=
strings
.
LastIndex
(
fname
,
string
(
os
.
PathSeparator
))
if
idx
!=
-
1
{
stack
=
append
(
stack
,
fname
[
idx
+
1
:
])
}
else
{
stack
=
append
(
stack
,
fname
)
}
}
}
return
stack
}
Godeps/_workspace/src/github.com/rjeczalik/notify/doc.go
0 → 100644
View file @
ef63e9af
// Copyright (c) 2014-2015 The Notify Authors. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
// Package notify implements access to filesystem events.
//
// Notify is a high-level abstraction over filesystem watchers like inotify,
// kqueue, FSEvents, FEN or ReadDirectoryChangesW. Watcher implementations are
// split into two groups: ones that natively support recursive notifications
// (FSEvents and ReadDirectoryChangesW) and ones that do not (inotify, kqueue, FEN).
// For more details see watcher and recursiveWatcher interfaces in watcher.go
// source file.
//
// On top of filesystem watchers notify maintains a watchpoint tree, which provides
// strategy for creating and closing filesystem watches and dispatching filesystem
// events to user channels.
//
// An event set is just an event list joint using bitwise OR operator
// into a single event value.
//
// A filesystem watch or just a watch is platform-specific entity which represents
// a single path registered for notifications for specific event set. Setting a watch
// means using platform-specific API calls for creating / initializing said watch.
// For each watcher the API call is:
//
// - FSEvents: FSEventStreamCreate
// - inotify: notify_add_watch
// - kqueue: kevent
// - ReadDirectoryChangesW: CreateFile+ReadDirectoryChangesW
// - FEN: port_get
//
// To rewatch means to either shrink or expand an event set that was previously
// registered during watch operation for particular filesystem watch.
//
// A watchpoint is a list of user channel and event set pairs for particular
// path (watchpoint tree's node). A single watchpoint can contain multiple
// different user channels registered to listen for one or more events. A single
// user channel can be registered in one or more watchpoints, recurisve and
// non-recursive ones as well.
package
notify
Godeps/_workspace/src/github.com/rjeczalik/notify/event.go
0 → 100644
View file @
ef63e9af
// Copyright (c) 2014-2015 The Notify Authors. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
package
notify
import
(
"fmt"
"strings"
)
// Event represents the type of filesystem action.
//
// Number of available event values is dependent on the target system or the
// watcher implmenetation used (e.g. it's possible to use either kqueue or
// FSEvents on Darwin).
//
// Please consult documentation for your target platform to see list of all
// available events.
type
Event
uint32
// Create, Remove, Write and Rename are the only event values guaranteed to be
// present on all platforms.
const
(
Create
=
osSpecificCreate
Remove
=
osSpecificRemove
Write
=
osSpecificWrite
Rename
=
osSpecificRename
// All is handful alias for all platform-independent event values.
All
=
Create
|
Remove
|
Write
|
Rename
)
const
internal
=
recursive
|
omit
// String implements fmt.Stringer interface.
func
(
e
Event
)
String
()
string
{
var
s
[]
string
for
_
,
strmap
:=
range
[]
map
[
Event
]
string
{
estr
,
osestr
}
{
for
ev
,
str
:=
range
strmap
{
if
e
&
ev
==
ev
{
s
=
append
(
s
,
str
)
}
}
}
return
strings
.
Join
(
s
,
"|"
)
}
// EventInfo describes an event reported by the underlying filesystem notification
// subsystem.
//
// It always describes single event, even if the OS reported a coalesced action.
// Reported path is absolute and clean.
//
// For non-recursive watchpoints its base is always equal to the path passed
// to corresponding Watch call.
//
// The value of Sys if system-dependent and can be nil.
//
// Sys
//
// Under Darwin (FSEvents) Sys() always returns a non-nil *notify.FSEvent value,
// which is defined as:
//
// type FSEvent struct {
// Path string // real path of the file or directory
// ID uint64 // ID of the event (FSEventStreamEventId)
// Flags uint32 // joint FSEvents* flags (FSEventStreamEventFlags)
// }
//
// For possible values of Flags see Darwin godoc for notify or FSEvents
// documentation for FSEventStreamEventFlags constants:
//
// https://developer.apple.com/library/mac/documentation/Darwin/Reference/FSEvents_Ref/index.html#//apple_ref/doc/constant_group/FSEventStreamEventFlags
//
// Under Linux (inotify) Sys() always returns a non-nil *syscall.InotifyEvent
// value, defined as:
//
// type InotifyEvent struct {
// Wd int32 // Watch descriptor
// Mask uint32 // Mask describing event
// Cookie uint32 // Unique cookie associating related events (for rename(2))
// Len uint32 // Size of name field
// Name [0]uint8 // Optional null-terminated name
// }
//
// More information about inotify masks and the usage of inotify_event structure
// can be found at:
//
// http://man7.org/linux/man-pages/man7/inotify.7.html
//
// Under Darwin, DragonFlyBSD, FreeBSD, NetBSD, OpenBSD (kqueue) Sys() always
// returns a non-nil *notify.Kevent value, which is defined as:
//
// type Kevent struct {
// Kevent *syscall.Kevent_t // Kevent is a kqueue specific structure
// FI os.FileInfo // FI describes file/dir
// }
//
// More information about syscall.Kevent_t can be found at:
//
// https://www.freebsd.org/cgi/man.cgi?query=kqueue
//
// Under Windows (ReadDirectoryChangesW) Sys() always returns nil. The documentation
// of watcher's WinAPI function can be found at:
//
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365465%28v=vs.85%29.aspx
type
EventInfo
interface
{
Event
()
Event
// event value for the filesystem action
Path
()
string
// real path of the file or directory
Sys
()
interface
{}
// underlying data source (can return nil)
}
type
isDirer
interface
{
isDir
()
(
bool
,
error
)
}
var
_
fmt
.
Stringer
=
(
*
event
)(
nil
)
var
_
isDirer
=
(
*
event
)(
nil
)
// String implements fmt.Stringer interface.
func
(
e
*
event
)
String
()
string
{
return
e
.
Event
()
.
String
()
+
`: "`
+
e
.
Path
()
+
`"`
}
var
estr
=
map
[
Event
]
string
{
Create
:
"notify.Create"
,
Remove
:
"notify.Remove"
,
Write
:
"notify.Write"
,
Rename
:
"notify.Rename"
,
// Display name for recursive event is added only for debugging
// purposes. It's an internal event after all and won't be exposed to the
// user. Having Recursive event printable is helpful, e.g. for reading
// testing failure messages:
//
// --- FAIL: TestWatchpoint (0.00 seconds)
// watchpoint_test.go:64: want diff=[notify.Remove notify.Create|notify.Remove];
// got [notify.Remove notify.Remove|notify.Create] (i=1)
//
// Yup, here the diff have Recursive event inside. Go figure.
recursive
:
"recursive"
,
omit
:
"omit"
,
}
Godeps/_workspace/src/github.com/rjeczalik/notify/event_fen.go
0 → 100644
View file @
ef63e9af
// Copyright (c) 2014-2015 The Notify Authors. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
// +build solaris
package
notify
const
(
osSpecificCreate
Event
=
0x00000100
<<
iota
osSpecificRemove
osSpecificWrite
osSpecificRename
// internal
// recursive is used to distinguish recursive eventsets from non-recursive ones
recursive
// omit is used for dispatching internal events; only those events are sent
// for which both the event and the watchpoint has omit in theirs event sets.
omit
)
const
(
FileAccess
=
fileAccess
FileModified
=
fileModified
FileAttrib
=
fileAttrib
FileDelete
=
fileDelete
FileRenameTo
=
fileRenameTo
FileRenameFrom
=
fileRenameFrom
FileTrunc
=
fileTrunc
FileNoFollow
=
fileNoFollow
Unmounted
=
unmounted
MountedOver
=
mountedOver
)
var
osestr
=
map
[
Event
]
string
{
FileAccess
:
"notify.FileAccess"
,
FileModified
:
"notify.FileModified"
,
FileAttrib
:
"notify.FileAttrib"
,
FileDelete
:
"notify.FileDelete"
,
FileRenameTo
:
"notify.FileRenameTo"
,
FileRenameFrom
:
"notify.FileRenameFrom"
,
FileTrunc
:
"notify.FileTrunc"
,
FileNoFollow
:
"notify.FileNoFollow"
,
Unmounted
:
"notify.Unmounted"
,
MountedOver
:
"notify.MountedOver"
,
}
Godeps/_workspace/src/github.com/rjeczalik/notify/event_fsevents.go
0 → 100644
View file @
ef63e9af
// Copyright (c) 2014-2015 The Notify Authors. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
// +build darwin,!kqueue
package
notify
const
(
osSpecificCreate
=
Event
(
FSEventsCreated
)
osSpecificRemove
=
Event
(
FSEventsRemoved
)
osSpecificWrite
=
Event
(
FSEventsModified
)
osSpecificRename
=
Event
(
FSEventsRenamed
)
// internal = Event(0x100000)
// recursive is used to distinguish recursive eventsets from non-recursive ones
recursive
=
Event
(
0x200000
)
// omit is used for dispatching internal events; only those events are sent
// for which both the event and the watchpoint has omit in theirs event sets.
omit
=
Event
(
0x400000
)
)
// FSEvents specific event values.
const
(
FSEventsMustScanSubDirs
Event
=
0x00001
FSEventsUserDropped
=
0x00002
FSEventsKernelDropped
=
0x00004
FSEventsEventIdsWrapped
=
0x00008
FSEventsHistoryDone
=
0x00010
FSEventsRootChanged
=
0x00020
FSEventsMount
=
0x00040
FSEventsUnmount
=
0x00080
FSEventsCreated
=
0x00100
FSEventsRemoved
=
0x00200
FSEventsInodeMetaMod
=
0x00400
FSEventsRenamed
=
0x00800
FSEventsModified
=
0x01000
FSEventsFinderInfoMod
=
0x02000
FSEventsChangeOwner
=
0x04000
FSEventsXattrMod
=
0x08000
FSEventsIsFile
=
0x10000
FSEventsIsDir
=
0x20000
FSEventsIsSymlink
=
0x40000
)
var
osestr
=
map
[
Event
]
string
{
FSEventsMustScanSubDirs
:
"notify.FSEventsMustScanSubDirs"
,
FSEventsUserDropped
:
"notify.FSEventsUserDropped"
,
FSEventsKernelDropped
:
"notify.FSEventsKernelDropped"
,
FSEventsEventIdsWrapped
:
"notify.FSEventsEventIdsWrapped"
,
FSEventsHistoryDone
:
"notify.FSEventsHistoryDone"
,
FSEventsRootChanged
:
"notify.FSEventsRootChanged"
,
FSEventsMount
:
"notify.FSEventsMount"
,
FSEventsUnmount
:
"notify.FSEventsUnmount"
,
FSEventsInodeMetaMod
:
"notify.FSEventsInodeMetaMod"
,
FSEventsFinderInfoMod
:
"notify.FSEventsFinderInfoMod"
,
FSEventsChangeOwner
:
"notify.FSEventsChangeOwner"
,
FSEventsXattrMod
:
"notify.FSEventsXattrMod"
,
FSEventsIsFile
:
"notify.FSEventsIsFile"
,
FSEventsIsDir
:
"notify.FSEventsIsDir"
,
FSEventsIsSymlink
:
"notify.FSEventsIsSymlink"
,
}
type
event
struct
{
fse
FSEvent
event
Event
}
func
(
ei
*
event
)
Event
()
Event
{
return
ei
.
event
}
func
(
ei
*
event
)
Path
()
string
{
return
ei
.
fse
.
Path
}
func
(
ei
*
event
)
Sys
()
interface
{}
{
return
&
ei
.
fse
}
func
(
ei
*
event
)
isDir
()
(
bool
,
error
)
{
return
ei
.
fse
.
Flags
&
FSEventsIsDir
!=
0
,
nil
}
Godeps/_workspace/src/github.com/rjeczalik/notify/event_inotify.go
0 → 100644
View file @
ef63e9af
// Copyright (c) 2014-2015 The Notify Authors. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
// +build linux
package
notify
import
"syscall"
// Platform independent event values.
const
(
osSpecificCreate
Event
=
0x100000
<<
iota
osSpecificRemove
osSpecificWrite
osSpecificRename
// internal
// recursive is used to distinguish recursive eventsets from non-recursive ones
recursive
// omit is used for dispatching internal events; only those events are sent
// for which both the event and the watchpoint has omit in theirs event sets.
omit
)
// Inotify specific masks are legal, implemented events that are guaranteed to
// work with notify package on linux-based systems.
const
(
InAccess
=
Event
(
syscall
.
IN_ACCESS
)
// File was accessed
InModify
=
Event
(
syscall
.
IN_MODIFY
)
// File was modified
InAttrib
=
Event
(
syscall
.
IN_ATTRIB
)
// Metadata changed
InCloseWrite
=
Event
(
syscall
.
IN_CLOSE_WRITE
)
// Writtable file was closed
InCloseNowrite
=
Event
(
syscall
.
IN_CLOSE_NOWRITE
)
// Unwrittable file closed
InOpen
=
Event
(
syscall
.
IN_OPEN
)
// File was opened
InMovedFrom
=
Event
(
syscall
.
IN_MOVED_FROM
)
// File was moved from X
InMovedTo
=
Event
(
syscall
.
IN_MOVED_TO
)
// File was moved to Y
InCreate
=
Event
(
syscall
.
IN_CREATE
)
// Subfile was created
InDelete
=
Event
(
syscall
.
IN_DELETE
)
// Subfile was deleted
InDeleteSelf
=
Event
(
syscall
.
IN_DELETE_SELF
)
// Self was deleted
InMoveSelf
=
Event
(
syscall
.
IN_MOVE_SELF
)
// Self was moved
)
var
osestr
=
map
[
Event
]
string
{
InAccess
:
"notify.InAccess"
,
InModify
:
"notify.InModify"
,
InAttrib
:
"notify.InAttrib"
,
InCloseWrite
:
"notify.InCloseWrite"
,
InCloseNowrite
:
"notify.InCloseNowrite"
,
InOpen
:
"notify.InOpen"
,
InMovedFrom
:
"notify.InMovedFrom"
,
InMovedTo
:
"notify.InMovedTo"
,
InCreate
:
"notify.InCreate"
,
InDelete
:
"notify.InDelete"
,
InDeleteSelf
:
"notify.InDeleteSelf"
,
InMoveSelf
:
"notify.InMoveSelf"
,
}
// Inotify behavior events are not **currently** supported by notify package.
const
(
inDontFollow
=
Event
(
syscall
.
IN_DONT_FOLLOW
)
inExclUnlink
=
Event
(
syscall
.
IN_EXCL_UNLINK
)
inMaskAdd
=
Event
(
syscall
.
IN_MASK_ADD
)
inOneshot
=
Event
(
syscall
.
IN_ONESHOT
)
inOnlydir
=
Event
(
syscall
.
IN_ONLYDIR
)
)
type
event
struct
{
sys
syscall
.
InotifyEvent
path
string
event
Event
}
func
(
e
*
event
)
Event
()
Event
{
return
e
.
event
}
func
(
e
*
event
)
Path
()
string
{
return
e
.
path
}
func
(
e
*
event
)
Sys
()
interface
{}
{
return
&
e
.
sys
}
func
(
e
*
event
)
isDir
()
(
bool
,
error
)
{
return
e
.
sys
.
Mask
&
syscall
.
IN_ISDIR
!=
0
,
nil
}
Godeps/_workspace/src/github.com/rjeczalik/notify/event_kqueue.go
0 → 100644
View file @
ef63e9af
// Copyright (c) 2014-2015 The Notify Authors. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
// +build darwin,kqueue dragonfly freebsd netbsd openbsd
package
notify
import
"syscall"
// TODO(pblaszczyk): ensure in runtime notify built-in event values do not
// overlap with platform-defined ones.
// Platform independent event values.
const
(
osSpecificCreate
Event
=
0x0100
<<
iota
osSpecificRemove
osSpecificWrite
osSpecificRename
// internal
// recursive is used to distinguish recursive eventsets from non-recursive ones
recursive
// omit is used for dispatching internal events; only those events are sent
// for which both the event and the watchpoint has omit in theirs event sets.
omit
)
const
(
// NoteDelete is an even reported when the unlink() system call was called
// on the file referenced by the descriptor.
NoteDelete
=
Event
(
syscall
.
NOTE_DELETE
)
// NoteWrite is an event reported when a write occurred on the file
// referenced by the descriptor.
NoteWrite
=
Event
(
syscall
.
NOTE_WRITE
)
// NoteExtend is an event reported when the file referenced by the
// descriptor was extended.
NoteExtend
=
Event
(
syscall
.
NOTE_EXTEND
)
// NoteAttrib is an event reported when the file referenced
// by the descriptor had its attributes changed.
NoteAttrib
=
Event
(
syscall
.
NOTE_ATTRIB
)
// NoteLink is an event reported when the link count on the file changed.
NoteLink
=
Event
(
syscall
.
NOTE_LINK
)
// NoteRename is an event reported when the file referenced
// by the descriptor was renamed.
NoteRename
=
Event
(
syscall
.
NOTE_RENAME
)
// NoteRevoke is an event reported when access to the file was revoked via
// revoke(2) or the underlying file system was unmounted.
NoteRevoke
=
Event
(
syscall
.
NOTE_REVOKE
)
)
var
osestr
=
map
[
Event
]
string
{
NoteDelete
:
"notify.NoteDelete"
,
NoteWrite
:
"notify.NoteWrite"
,
NoteExtend
:
"notify.NoteExtend"
,
NoteAttrib
:
"notify.NoteAttrib"
,
NoteLink
:
"notify.NoteLink"
,
NoteRename
:
"notify.NoteRename"
,
NoteRevoke
:
"notify.NoteRevoke"
,
}
Godeps/_workspace/src/github.com/rjeczalik/notify/event_readdcw.go
0 → 100644
View file @
ef63e9af
// Copyright (c) 2014-2015 The Notify Authors. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
// +build windows
package
notify
import
(
"os"
"path/filepath"
"syscall"
)
// Platform independent event values.
const
(
osSpecificCreate
Event
=
1
<<
(
20
+
iota
)
osSpecificRemove
osSpecificWrite
osSpecificRename
// recursive is used to distinguish recursive eventsets from non-recursive ones
recursive
// omit is used for dispatching internal events; only those events are sent
// for which both the event and the watchpoint has omit in theirs event sets.
omit
// dirmarker TODO(pknap)
dirmarker
)
// ReadDirectoryChangesW filters.
const
(
FileNotifyChangeFileName
=
Event
(
syscall
.
FILE_NOTIFY_CHANGE_FILE_NAME
)
FileNotifyChangeDirName
=
Event
(
syscall
.
FILE_NOTIFY_CHANGE_DIR_NAME
)
FileNotifyChangeAttributes
=
Event
(
syscall
.
FILE_NOTIFY_CHANGE_ATTRIBUTES
)
FileNotifyChangeSize
=
Event
(
syscall
.
FILE_NOTIFY_CHANGE_SIZE
)
FileNotifyChangeLastWrite
=
Event
(
syscall
.
FILE_NOTIFY_CHANGE_LAST_WRITE
)
FileNotifyChangeLastAccess
=
Event
(
syscall
.
FILE_NOTIFY_CHANGE_LAST_ACCESS
)
FileNotifyChangeCreation
=
Event
(
syscall
.
FILE_NOTIFY_CHANGE_CREATION
)
FileNotifyChangeSecurity
=
Event
(
syscallFileNotifyChangeSecurity
)
)
const
(
fileNotifyChangeAll
=
0x17f
// logical sum of all FileNotifyChange* events.
fileNotifyChangeModified
=
fileNotifyChangeAll
&^
(
FileNotifyChangeFileName
|
FileNotifyChangeDirName
)
)
// according to: http://msdn.microsoft.com/en-us/library/windows/desktop/aa365465(v=vs.85).aspx
// this flag should be declared in: http://golang.org/src/pkg/syscall/ztypes_windows.go
const
syscallFileNotifyChangeSecurity
=
0x00000100
// ReadDirectoryChangesW actions.
const
(
FileActionAdded
=
Event
(
syscall
.
FILE_ACTION_ADDED
)
<<
12
FileActionRemoved
=
Event
(
syscall
.
FILE_ACTION_REMOVED
)
<<
12
FileActionModified
=
Event
(
syscall
.
FILE_ACTION_MODIFIED
)
<<
14
FileActionRenamedOldName
=
Event
(
syscall
.
FILE_ACTION_RENAMED_OLD_NAME
)
<<
15
FileActionRenamedNewName
=
Event
(
syscall
.
FILE_ACTION_RENAMED_NEW_NAME
)
<<
16
)
const
fileActionAll
=
0x7f000
// logical sum of all FileAction* events.
var
osestr
=
map
[
Event
]
string
{
FileNotifyChangeFileName
:
"notify.FileNotifyChangeFileName"
,
FileNotifyChangeDirName
:
"notify.FileNotifyChangeDirName"
,
FileNotifyChangeAttributes
:
"notify.FileNotifyChangeAttributes"
,
FileNotifyChangeSize
:
"notify.FileNotifyChangeSize"
,
FileNotifyChangeLastWrite
:
"notify.FileNotifyChangeLastWrite"
,
FileNotifyChangeLastAccess
:
"notify.FileNotifyChangeLastAccess"
,
FileNotifyChangeCreation
:
"notify.FileNotifyChangeCreation"
,
FileNotifyChangeSecurity
:
"notify.FileNotifyChangeSecurity"
,
FileActionAdded
:
"notify.FileActionAdded"
,
FileActionRemoved
:
"notify.FileActionRemoved"
,
FileActionModified
:
"notify.FileActionModified"
,
FileActionRenamedOldName
:
"notify.FileActionRenamedOldName"
,
FileActionRenamedNewName
:
"notify.FileActionRenamedNewName"
,
}
const
(
fTypeUnknown
uint8
=
iota
fTypeFile
fTypeDirectory
)
// TODO(ppknap) : doc.
type
event
struct
{
pathw
[]
uint16
name
string
ftype
uint8
action
uint32
filter
uint32
e
Event
}
func
(
e
*
event
)
Event
()
Event
{
return
e
.
e
}
func
(
e
*
event
)
Path
()
string
{
return
filepath
.
Join
(
syscall
.
UTF16ToString
(
e
.
pathw
),
e
.
name
)
}
func
(
e
*
event
)
Sys
()
interface
{}
{
return
e
.
ftype
}
func
(
e
*
event
)
isDir
()
(
bool
,
error
)
{
if
e
.
ftype
!=
fTypeUnknown
{
return
e
.
ftype
==
fTypeDirectory
,
nil
}
fi
,
err
:=
os
.
Stat
(
e
.
Path
())
if
err
!=
nil
{
return
false
,
err
}
return
fi
.
IsDir
(),
nil
}
Godeps/_workspace/src/github.com/rjeczalik/notify/event_stub.go
0 → 100644
View file @
ef63e9af
// Copyright (c) 2014-2015 The Notify Authors. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
// +build !darwin,!linux,!freebsd,!dragonfly,!netbsd,!openbsd,!windows
// +build !kqueue,!solaris
package
notify
// Platform independent event values.
const
(
osSpecificCreate
Event
=
1
<<
iota
osSpecificRemove
osSpecificWrite
osSpecificRename
// internal
// recursive is used to distinguish recursive eventsets from non-recursive ones
recursive
// omit is used for dispatching internal events; only those events are sent
// for which both the event and the watchpoint has omit in theirs event sets.
omit
)
var
osestr
=
map
[
Event
]
string
{}
type
event
struct
{}
func
(
e
*
event
)
Event
()
(
_
Event
)
{
return
}
func
(
e
*
event
)
Path
()
(
_
string
)
{
return
}
func
(
e
*
event
)
Sys
()
(
_
interface
{})
{
return
}
func
(
e
*
event
)
isDir
()
(
_
bool
,
_
error
)
{
return
}
Godeps/_workspace/src/github.com/rjeczalik/notify/event_trigger.go
0 → 100644
View file @
ef63e9af
// Copyright (c) 2014-2015 The Notify Authors. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
// +build darwin,kqueue dragonfly freebsd netbsd openbsd solaris
package
notify
type
event
struct
{
p
string
e
Event
d
bool
pe
interface
{}
}
func
(
e
*
event
)
Event
()
Event
{
return
e
.
e
}
func
(
e
*
event
)
Path
()
string
{
return
e
.
p
}
func
(
e
*
event
)
Sys
()
interface
{}
{
return
e
.
pe
}
func
(
e
*
event
)
isDir
()
(
bool
,
error
)
{
return
e
.
d
,
nil
}
Godeps/_workspace/src/github.com/rjeczalik/notify/node.go
0 → 100644
View file @
ef63e9af
// Copyright (c) 2014-2015 The Notify Authors. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
package
notify
import
(
"errors"
"io/ioutil"
"os"
"path/filepath"
)
var
errSkip
=
errors
.
New
(
"notify: skip"
)
type
walkPathFunc
func
(
nd
node
,
isbase
bool
)
error
type
walkFunc
func
(
node
)
error
func
errnotexist
(
name
string
)
error
{
return
&
os
.
PathError
{
Op
:
"Node"
,
Path
:
name
,
Err
:
os
.
ErrNotExist
,
}
}
type
node
struct
{
Name
string
Watch
watchpoint
Child
map
[
string
]
node
}
func
newnode
(
name
string
)
node
{
return
node
{
Name
:
name
,
Watch
:
make
(
watchpoint
),
Child
:
make
(
map
[
string
]
node
),
}
}
func
(
nd
node
)
addchild
(
name
,
base
string
)
node
{
child
,
ok
:=
nd
.
Child
[
base
]
if
!
ok
{
child
=
newnode
(
name
)
nd
.
Child
[
base
]
=
child
}
return
child
}
func
(
nd
node
)
Add
(
name
string
)
node
{
i
:=
indexbase
(
nd
.
Name
,
name
)
if
i
==
-
1
{
return
node
{}
}
for
j
:=
indexSep
(
name
[
i
:
]);
j
!=
-
1
;
j
=
indexSep
(
name
[
i
:
])
{
nd
=
nd
.
addchild
(
name
[
:
i
+
j
],
name
[
i
:
i
+
j
])
i
+=
j
+
1
}
return
nd
.
addchild
(
name
,
name
[
i
:
])
}
func
(
nd
node
)
AddDir
(
fn
walkFunc
)
error
{
stack
:=
[]
node
{
nd
}
Traverse
:
for
n
:=
len
(
stack
);
n
!=
0
;
n
=
len
(
stack
)
{
nd
,
stack
=
stack
[
n
-
1
],
stack
[
:
n
-
1
]
switch
err
:=
fn
(
nd
);
err
{
case
nil
:
case
errSkip
:
continue
Traverse
default
:
return
err
}
// TODO(rjeczalik): tolerate open failures - add failed names to
// AddDirError and notify users which names are not added to the tree.
fi
,
err
:=
ioutil
.
ReadDir
(
nd
.
Name
)
if
err
!=
nil
{
return
err
}
for
_
,
fi
:=
range
fi
{
if
fi
.
Mode
()
&
(
os
.
ModeSymlink
|
os
.
ModeDir
)
==
os
.
ModeDir
{
name
:=
filepath
.
Join
(
nd
.
Name
,
fi
.
Name
())
stack
=
append
(
stack
,
nd
.
addchild
(
name
,
name
[
len
(
nd
.
Name
)
+
1
:
]))
}
}
}
return
nil
}
func
(
nd
node
)
Get
(
name
string
)
(
node
,
error
)
{
i
:=
indexbase
(
nd
.
Name
,
name
)
if
i
==
-
1
{
return
node
{},
errnotexist
(
name
)
}
ok
:=
false
for
j
:=
indexSep
(
name
[
i
:
]);
j
!=
-
1
;
j
=
indexSep
(
name
[
i
:
])
{
if
nd
,
ok
=
nd
.
Child
[
name
[
i
:
i
+
j
]];
!
ok
{
return
node
{},
errnotexist
(
name
)
}
i
+=
j
+
1
}
if
nd
,
ok
=
nd
.
Child
[
name
[
i
:
]];
!
ok
{
return
node
{},
errnotexist
(
name
)
}
return
nd
,
nil
}
func
(
nd
node
)
Del
(
name
string
)
error
{
i
:=
indexbase
(
nd
.
Name
,
name
)
if
i
==
-
1
{
return
errnotexist
(
name
)
}
stack
:=
[]
node
{
nd
}
ok
:=
false
for
j
:=
indexSep
(
name
[
i
:
]);
j
!=
-
1
;
j
=
indexSep
(
name
[
i
:
])
{
if
nd
,
ok
=
nd
.
Child
[
name
[
i
:
i
+
j
]];
!
ok
{
return
errnotexist
(
name
[
:
i
+
j
])
}
stack
=
append
(
stack
,
nd
)
}
if
nd
,
ok
=
nd
.
Child
[
name
[
i
:
]];
!
ok
{
return
errnotexist
(
name
)
}
nd
.
Child
=
nil
nd
.
Watch
=
nil
for
name
,
i
=
base
(
nd
.
Name
),
len
(
stack
);
i
!=
0
;
name
,
i
=
base
(
nd
.
Name
),
i
-
1
{
nd
=
stack
[
i
-
1
]
if
nd
:=
nd
.
Child
[
name
];
len
(
nd
.
Watch
)
>
1
||
len
(
nd
.
Child
)
!=
0
{
break
}
else
{
nd
.
Child
=
nil
nd
.
Watch
=
nil
}
delete
(
nd
.
Child
,
name
)
}
return
nil
}
func
(
nd
node
)
Walk
(
fn
walkFunc
)
error
{
stack
:=
[]
node
{
nd
}
Traverse
:
for
n
:=
len
(
stack
);
n
!=
0
;
n
=
len
(
stack
)
{
nd
,
stack
=
stack
[
n
-
1
],
stack
[
:
n
-
1
]
switch
err
:=
fn
(
nd
);
err
{
case
nil
:
case
errSkip
:
continue
Traverse
default
:
return
err
}
for
name
,
nd
:=
range
nd
.
Child
{
if
name
==
""
{
// Node storing inactive watchpoints has empty name, skip it
// form traversing. Root node has also an empty name, but it
// never has a parent node.
continue
}
stack
=
append
(
stack
,
nd
)
}
}
return
nil
}
func
(
nd
node
)
WalkPath
(
name
string
,
fn
walkPathFunc
)
error
{
i
:=
indexbase
(
nd
.
Name
,
name
)
if
i
==
-
1
{
return
errnotexist
(
name
)
}
ok
:=
false
for
j
:=
indexSep
(
name
[
i
:
]);
j
!=
-
1
;
j
=
indexSep
(
name
[
i
:
])
{
switch
err
:=
fn
(
nd
,
false
);
err
{
case
nil
:
case
errSkip
:
return
nil
default
:
return
err
}
if
nd
,
ok
=
nd
.
Child
[
name
[
i
:
i
+
j
]];
!
ok
{
return
errnotexist
(
name
[
:
i
+
j
])
}
i
+=
j
+
1
}
switch
err
:=
fn
(
nd
,
false
);
err
{
case
nil
:
case
errSkip
:
return
nil
default
:
return
err
}
if
nd
,
ok
=
nd
.
Child
[
name
[
i
:
]];
!
ok
{
return
errnotexist
(
name
)
}
switch
err
:=
fn
(
nd
,
true
);
err
{
case
nil
,
errSkip
:
return
nil
default
:
return
err
}
}
type
root
struct
{
nd
node
}
func
(
r
root
)
addroot
(
name
string
)
node
{
if
vol
:=
filepath
.
VolumeName
(
name
);
vol
!=
""
{
root
,
ok
:=
r
.
nd
.
Child
[
vol
]
if
!
ok
{
root
=
r
.
nd
.
addchild
(
vol
,
vol
)
}
return
root
}
return
r
.
nd
}
func
(
r
root
)
root
(
name
string
)
(
node
,
error
)
{
if
vol
:=
filepath
.
VolumeName
(
name
);
vol
!=
""
{
nd
,
ok
:=
r
.
nd
.
Child
[
vol
]
if
!
ok
{
return
node
{},
errnotexist
(
name
)
}
return
nd
,
nil
}
return
r
.
nd
,
nil
}
func
(
r
root
)
Add
(
name
string
)
node
{
return
r
.
addroot
(
name
)
.
Add
(
name
)
}
func
(
r
root
)
AddDir
(
dir
string
,
fn
walkFunc
)
error
{
return
r
.
Add
(
dir
)
.
AddDir
(
fn
)
}
func
(
r
root
)
Del
(
name
string
)
error
{
nd
,
err
:=
r
.
root
(
name
)
if
err
!=
nil
{
return
err
}
return
nd
.
Del
(
name
)
}
func
(
r
root
)
Get
(
name
string
)
(
node
,
error
)
{
nd
,
err
:=
r
.
root
(
name
)
if
err
!=
nil
{
return
node
{},
err
}
if
nd
.
Name
!=
name
{
if
nd
,
err
=
nd
.
Get
(
name
);
err
!=
nil
{
return
node
{},
err
}
}
return
nd
,
nil
}
func
(
r
root
)
Walk
(
name
string
,
fn
walkFunc
)
error
{
nd
,
err
:=
r
.
Get
(
name
)
if
err
!=
nil
{
return
err
}
return
nd
.
Walk
(
fn
)
}
func
(
r
root
)
WalkPath
(
name
string
,
fn
walkPathFunc
)
error
{
nd
,
err
:=
r
.
root
(
name
)
if
err
!=
nil
{
return
err
}
return
nd
.
WalkPath
(
name
,
fn
)
}
Godeps/_workspace/src/github.com/rjeczalik/notify/notify.go
0 → 100644
View file @
ef63e9af
// Copyright (c) 2014-2015 The Notify Authors. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
// BUG(rjeczalik): Notify does not collect watchpoints, when underlying watches
// were removed by their os-specific watcher implementations. Instead users are
// advised to listen on persistant paths to have guarantee they receive events
// for the whole lifetime of their applications (to discuss see #69).
// BUG(ppknap): Linux (inotify) does not support watcher behavior masks like
// InOneshot, InOnlydir etc. Instead users are advised to perform the filtering
// themselves (to discuss see #71).
// BUG(ppknap): Notify was not tested for short path name support under Windows
// (ReadDirectoryChangesW).
// BUG(ppknap): Windows (ReadDirectoryChangesW) cannot recognize which notification
// triggers FileActionModified event. (to discuss see #75).
package
notify
var
defaultTree
=
newTree
()
// Watch sets up a watchpoint on path listening for events given by the events
// argument.
//
// File or directory given by the path must exist, otherwise Watch will fail
// with non-nil error. Notify resolves, for its internal purpose, any symlinks
// the provided path may contain, so it may fail if the symlinks form a cycle.
// It does so, since not all watcher implementations treat passed paths as-is.
// E.g. FSEvents reports a real path for every event, setting a watchpoint
// on /tmp will report events with paths rooted at /private/tmp etc.
//
// The c almost always is a buffered channel. Watch will not block sending to c
// - the caller must ensure that c has sufficient buffer space to keep up with
// the expected event rate.
//
// It is allowed to pass the same channel multiple times with different event
// list or different paths. Calling Watch with different event lists for a single
// watchpoint expands its event set. The only way to shrink it, is to call
// Stop on its channel.
//
// Calling Watch with empty event list does expand nor shrink watchpoint's event
// set. If c is the first channel to listen for events on the given path, Watch
// will seamlessly create a watch on the filesystem.
//
// Notify dispatches copies of single filesystem event to all channels registered
// for each path. If a single filesystem event contains multiple coalesced events,
// each of them is dispatched separately. E.g. the following filesystem change:
//
// ~ $ echo Hello > Notify.txt
//
// dispatches two events - notify.Create and notify.Write. However, it may depend
// on the underlying watcher implementation whether OS reports both of them.
//
// Windows and recursive watches
//
// If a directory which path was used to create recursive watch under Windows
// gets deleted, the OS will not report such event. It is advised to keep in
// mind this limitation while setting recursive watchpoints for your application,
// e.g. use persistant paths like %userprofile% or watch additionally parent
// directory of a recursive watchpoint in order to receive delete events for it.
func
Watch
(
path
string
,
c
chan
<-
EventInfo
,
events
...
Event
)
error
{
return
defaultTree
.
Watch
(
path
,
c
,
events
...
)
}
// Stop removes all watchpoints registered for c. All underlying watches are
// also removed, for which c was the last channel listening for events.
//
// Stop does not close c. When Stop returns, it is guranteed that c will
// receive no more signals.
func
Stop
(
c
chan
<-
EventInfo
)
{
defaultTree
.
Stop
(
c
)
}
Godeps/_workspace/src/github.com/rjeczalik/notify/tree.go
0 → 100644
View file @
ef63e9af
// Copyright (c) 2014-2015 The Notify Authors. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
package
notify
const
buffer
=
128
type
tree
interface
{
Watch
(
string
,
chan
<-
EventInfo
,
...
Event
)
error
Stop
(
chan
<-
EventInfo
)
Close
()
error
}
func
newTree
()
tree
{
c
:=
make
(
chan
EventInfo
,
buffer
)
w
:=
newWatcher
(
c
)
if
rw
,
ok
:=
w
.
(
recursiveWatcher
);
ok
{
return
newRecursiveTree
(
rw
,
c
)
}
return
newNonrecursiveTree
(
w
,
c
,
make
(
chan
EventInfo
,
buffer
))
}
Godeps/_workspace/src/github.com/rjeczalik/notify/tree_nonrecursive.go
0 → 100644
View file @
ef63e9af
// Copyright (c) 2014-2015 The Notify Authors. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
package
notify
import
"sync"
// nonrecursiveTree TODO(rjeczalik)
type
nonrecursiveTree
struct
{
rw
sync
.
RWMutex
// protects root
root
root
w
watcher
c
chan
EventInfo
rec
chan
EventInfo
}
// newNonrecursiveTree TODO(rjeczalik)
func
newNonrecursiveTree
(
w
watcher
,
c
,
rec
chan
EventInfo
)
*
nonrecursiveTree
{
if
rec
==
nil
{
rec
=
make
(
chan
EventInfo
,
buffer
)
}
t
:=
&
nonrecursiveTree
{
root
:
root
{
nd
:
newnode
(
""
)},
w
:
w
,
c
:
c
,
rec
:
rec
,
}
go
t
.
dispatch
(
c
)
go
t
.
internal
(
rec
)
return
t
}
// dispatch TODO(rjeczalik)
func
(
t
*
nonrecursiveTree
)
dispatch
(
c
<-
chan
EventInfo
)
{
for
ei
:=
range
c
{
dbgprintf
(
"dispatching %v on %q"
,
ei
.
Event
(),
ei
.
Path
())
go
func
(
ei
EventInfo
)
{
var
nd
node
var
isrec
bool
dir
,
base
:=
split
(
ei
.
Path
())
fn
:=
func
(
it
node
,
isbase
bool
)
error
{
isrec
=
isrec
||
it
.
Watch
.
IsRecursive
()
if
isbase
{
nd
=
it
}
else
{
it
.
Watch
.
Dispatch
(
ei
,
recursive
)
}
return
nil
}
t
.
rw
.
RLock
()
// Notify recursive watchpoints found on the path.
if
err
:=
t
.
root
.
WalkPath
(
dir
,
fn
);
err
!=
nil
{
dbgprint
(
"dispatch did not reach leaf:"
,
err
)
t
.
rw
.
RUnlock
()
return
}
// Notify parent watchpoint.
nd
.
Watch
.
Dispatch
(
ei
,
0
)
isrec
=
isrec
||
nd
.
Watch
.
IsRecursive
()
// If leaf watchpoint exists, notify it.
if
nd
,
ok
:=
nd
.
Child
[
base
];
ok
{
isrec
=
isrec
||
nd
.
Watch
.
IsRecursive
()
nd
.
Watch
.
Dispatch
(
ei
,
0
)
}
t
.
rw
.
RUnlock
()
// If the event describes newly leaf directory created within
if
!
isrec
||
ei
.
Event
()
!=
Create
{
return
}
if
ok
,
err
:=
ei
.
(
isDirer
)
.
isDir
();
!
ok
||
err
!=
nil
{
return
}
t
.
rec
<-
ei
}(
ei
)
}
}
// internal TODO(rjeczalik)
func
(
t
*
nonrecursiveTree
)
internal
(
rec
<-
chan
EventInfo
)
{
for
ei
:=
range
rec
{
var
nd
node
var
eset
=
internal
t
.
rw
.
Lock
()
t
.
root
.
WalkPath
(
ei
.
Path
(),
func
(
it
node
,
_
bool
)
error
{
if
e
:=
it
.
Watch
[
t
.
rec
];
e
!=
0
&&
e
>
eset
{
eset
=
e
}
nd
=
it
return
nil
})
if
eset
==
internal
{
t
.
rw
.
Unlock
()
continue
}
err
:=
nd
.
Add
(
ei
.
Path
())
.
AddDir
(
t
.
recFunc
(
eset
))
t
.
rw
.
Unlock
()
if
err
!=
nil
{
dbgprintf
(
"internal(%p) error: %v"
,
rec
,
err
)
}
}
}
// watchAdd TODO(rjeczalik)
func
(
t
*
nonrecursiveTree
)
watchAdd
(
nd
node
,
c
chan
<-
EventInfo
,
e
Event
)
eventDiff
{
if
e
&
recursive
!=
0
{
diff
:=
nd
.
Watch
.
Add
(
t
.
rec
,
e
|
Create
|
omit
)
nd
.
Watch
.
Add
(
c
,
e
)
return
diff
}
return
nd
.
Watch
.
Add
(
c
,
e
)
}
// watchDelMin TODO(rjeczalik)
func
(
t
*
nonrecursiveTree
)
watchDelMin
(
min
Event
,
nd
node
,
c
chan
<-
EventInfo
,
e
Event
)
eventDiff
{
old
,
ok
:=
nd
.
Watch
[
t
.
rec
]
if
ok
{
nd
.
Watch
[
t
.
rec
]
=
min
}
diff
:=
nd
.
Watch
.
Del
(
c
,
e
)
if
ok
{
switch
old
&^=
diff
[
0
]
&^
diff
[
1
];
{
case
old
|
internal
==
internal
:
delete
(
nd
.
Watch
,
t
.
rec
)
if
set
,
ok
:=
nd
.
Watch
[
nil
];
ok
&&
len
(
nd
.
Watch
)
==
1
&&
set
==
0
{
delete
(
nd
.
Watch
,
nil
)
}
default
:
nd
.
Watch
.
Add
(
t
.
rec
,
old
|
Create
)
switch
{
case
diff
==
none
:
case
diff
[
1
]
|
Create
==
diff
[
0
]
:
diff
=
none
default
:
diff
[
1
]
|=
Create
}
}
}
return
diff
}
// watchDel TODO(rjeczalik)
func
(
t
*
nonrecursiveTree
)
watchDel
(
nd
node
,
c
chan
<-
EventInfo
,
e
Event
)
eventDiff
{
return
t
.
watchDelMin
(
0
,
nd
,
c
,
e
)
}
// Watch TODO(rjeczalik)
func
(
t
*
nonrecursiveTree
)
Watch
(
path
string
,
c
chan
<-
EventInfo
,
events
...
Event
)
error
{
if
c
==
nil
{
panic
(
"notify: Watch using nil channel"
)
}
// Expanding with empty event set is a nop.
if
len
(
events
)
==
0
{
return
nil
}
path
,
isrec
,
err
:=
cleanpath
(
path
)
if
err
!=
nil
{
return
err
}
eset
:=
joinevents
(
events
)
t
.
rw
.
Lock
()
defer
t
.
rw
.
Unlock
()
nd
:=
t
.
root
.
Add
(
path
)
if
isrec
{
return
t
.
watchrec
(
nd
,
c
,
eset
|
recursive
)
}
return
t
.
watch
(
nd
,
c
,
eset
)
}
func
(
t
*
nonrecursiveTree
)
watch
(
nd
node
,
c
chan
<-
EventInfo
,
e
Event
)
(
err
error
)
{
diff
:=
nd
.
Watch
.
Add
(
c
,
e
)
switch
{
case
diff
==
none
:
return
nil
case
diff
[
1
]
==
0
:
// TODO(rjeczalik): cleanup this panic after implementation is stable
panic
(
"eset is empty: "
+
nd
.
Name
)
case
diff
[
0
]
==
0
:
err
=
t
.
w
.
Watch
(
nd
.
Name
,
diff
[
1
])
default
:
err
=
t
.
w
.
Rewatch
(
nd
.
Name
,
diff
[
0
],
diff
[
1
])
}
if
err
!=
nil
{
nd
.
Watch
.
Del
(
c
,
diff
.
Event
())
return
err
}
return
nil
}
func
(
t
*
nonrecursiveTree
)
recFunc
(
e
Event
)
walkFunc
{
return
func
(
nd
node
)
error
{
switch
diff
:=
nd
.
Watch
.
Add
(
t
.
rec
,
e
|
omit
|
Create
);
{
case
diff
==
none
:
case
diff
[
1
]
==
0
:
// TODO(rjeczalik): cleanup this panic after implementation is stable
panic
(
"eset is empty: "
+
nd
.
Name
)
case
diff
[
0
]
==
0
:
t
.
w
.
Watch
(
nd
.
Name
,
diff
[
1
])
default
:
t
.
w
.
Rewatch
(
nd
.
Name
,
diff
[
0
],
diff
[
1
])
}
return
nil
}
}
func
(
t
*
nonrecursiveTree
)
watchrec
(
nd
node
,
c
chan
<-
EventInfo
,
e
Event
)
error
{
var
traverse
func
(
walkFunc
)
error
// Non-recursive tree listens on Create event for every recursive
// watchpoint in order to automagically set a watch for every
// created directory.
switch
diff
:=
nd
.
Watch
.
dryAdd
(
t
.
rec
,
e
|
Create
);
{
case
diff
==
none
:
t
.
watchAdd
(
nd
,
c
,
e
)
nd
.
Watch
.
Add
(
t
.
rec
,
e
|
omit
|
Create
)
return
nil
case
diff
[
1
]
==
0
:
// TODO(rjeczalik): cleanup this panic after implementation is stable
panic
(
"eset is empty: "
+
nd
.
Name
)
case
diff
[
0
]
==
0
:
// TODO(rjeczalik): BFS into directories and skip subtree as soon as first
// recursive watchpoint is encountered.
traverse
=
nd
.
AddDir
default
:
traverse
=
nd
.
Walk
}
// TODO(rjeczalik): account every path that failed to be (re)watched
// and retry.
if
err
:=
traverse
(
t
.
recFunc
(
e
));
err
!=
nil
{
return
err
}
t
.
watchAdd
(
nd
,
c
,
e
)
return
nil
}
type
walkWatchpointFunc
func
(
Event
,
node
)
error
func
(
t
*
nonrecursiveTree
)
walkWatchpoint
(
nd
node
,
fn
walkWatchpointFunc
)
error
{
type
minode
struct
{
min
Event
nd
node
}
mnd
:=
minode
{
nd
:
nd
}
stack
:=
[]
minode
{
mnd
}
Traverse
:
for
n
:=
len
(
stack
);
n
!=
0
;
n
=
len
(
stack
)
{
mnd
,
stack
=
stack
[
n
-
1
],
stack
[
:
n
-
1
]
// There must be no recursive watchpoints if the node has no watchpoints
// itself (every node in subtree rooted at recursive watchpoints must
// have at least nil (total) and t.rec watchpoints).
if
len
(
mnd
.
nd
.
Watch
)
!=
0
{
switch
err
:=
fn
(
mnd
.
min
,
mnd
.
nd
);
err
{
case
nil
:
case
errSkip
:
continue
Traverse
default
:
return
err
}
}
for
_
,
nd
:=
range
mnd
.
nd
.
Child
{
stack
=
append
(
stack
,
minode
{
mnd
.
nd
.
Watch
[
t
.
rec
],
nd
})
}
}
return
nil
}
// Stop TODO(rjeczalik)
func
(
t
*
nonrecursiveTree
)
Stop
(
c
chan
<-
EventInfo
)
{
fn
:=
func
(
min
Event
,
nd
node
)
error
{
// TODO(rjeczalik): aggregate watcher errors and retry; in worst case
// forward to the user.
switch
diff
:=
t
.
watchDelMin
(
min
,
nd
,
c
,
all
);
{
case
diff
==
none
:
return
nil
case
diff
[
1
]
==
0
:
t
.
w
.
Unwatch
(
nd
.
Name
)
default
:
t
.
w
.
Rewatch
(
nd
.
Name
,
diff
[
0
],
diff
[
1
])
}
return
nil
}
t
.
rw
.
Lock
()
err
:=
t
.
walkWatchpoint
(
t
.
root
.
nd
,
fn
)
// TODO(rjeczalik): store max root per c
t
.
rw
.
Unlock
()
dbgprintf
(
"Stop(%p) error: %v
\n
"
,
c
,
err
)
}
// Close TODO(rjeczalik)
func
(
t
*
nonrecursiveTree
)
Close
()
error
{
err
:=
t
.
w
.
Close
()
close
(
t
.
c
)
return
err
}
Godeps/_workspace/src/github.com/rjeczalik/notify/tree_recursive.go
0 → 100644
View file @
ef63e9af
// Copyright (c) 2014-2015 The Notify Authors. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
package
notify
import
"sync"
// watchAdd TODO(rjeczalik)
func
watchAdd
(
nd
node
,
c
chan
<-
EventInfo
,
e
Event
)
eventDiff
{
diff
:=
nd
.
Watch
.
Add
(
c
,
e
)
if
wp
:=
nd
.
Child
[
""
]
.
Watch
;
len
(
wp
)
!=
0
{
e
=
wp
.
Total
()
diff
[
0
]
|=
e
diff
[
1
]
|=
e
if
diff
[
0
]
==
diff
[
1
]
{
return
none
}
}
return
diff
}
// watchAddInactive TODO(rjeczalik)
func
watchAddInactive
(
nd
node
,
c
chan
<-
EventInfo
,
e
Event
)
eventDiff
{
wp
:=
nd
.
Child
[
""
]
.
Watch
if
wp
==
nil
{
wp
=
make
(
watchpoint
)
nd
.
Child
[
""
]
=
node
{
Watch
:
wp
}
}
diff
:=
wp
.
Add
(
c
,
e
)
e
=
nd
.
Watch
.
Total
()
diff
[
0
]
|=
e
diff
[
1
]
|=
e
if
diff
[
0
]
==
diff
[
1
]
{
return
none
}
return
diff
}
// watchCopy TODO(rjeczalik)
func
watchCopy
(
src
,
dst
node
)
{
for
c
,
e
:=
range
src
.
Watch
{
if
c
==
nil
{
continue
}
watchAddInactive
(
dst
,
c
,
e
)
}
if
wpsrc
:=
src
.
Child
[
""
]
.
Watch
;
len
(
wpsrc
)
!=
0
{
wpdst
:=
dst
.
Child
[
""
]
.
Watch
for
c
,
e
:=
range
wpsrc
{
if
c
==
nil
{
continue
}
wpdst
.
Add
(
c
,
e
)
}
}
}
// watchDel TODO(rjeczalik)
func
watchDel
(
nd
node
,
c
chan
<-
EventInfo
,
e
Event
)
eventDiff
{
diff
:=
nd
.
Watch
.
Del
(
c
,
e
)
if
wp
:=
nd
.
Child
[
""
]
.
Watch
;
len
(
wp
)
!=
0
{
diffInactive
:=
wp
.
Del
(
c
,
e
)
e
=
wp
.
Total
()
// TODO(rjeczalik): add e if e != all?
diff
[
0
]
|=
diffInactive
[
0
]
|
e
diff
[
1
]
|=
diffInactive
[
1
]
|
e
if
diff
[
0
]
==
diff
[
1
]
{
return
none
}
}
return
diff
}
// watchTotal TODO(rjeczalik)
func
watchTotal
(
nd
node
)
Event
{
e
:=
nd
.
Watch
.
Total
()
if
wp
:=
nd
.
Child
[
""
]
.
Watch
;
len
(
wp
)
!=
0
{
e
|=
wp
.
Total
()
}
return
e
}
// watchIsRecursive TODO(rjeczalik)
func
watchIsRecursive
(
nd
node
)
bool
{
ok
:=
nd
.
Watch
.
IsRecursive
()
// TODO(rjeczalik): add a test for len(wp) != 0 change the condition.
if
wp
:=
nd
.
Child
[
""
]
.
Watch
;
len
(
wp
)
!=
0
{
// If a watchpoint holds inactive watchpoints, it means it's a parent
// one, which is recursive by nature even though it may be not recursive
// itself.
ok
=
true
}
return
ok
}
// recursiveTree TODO(rjeczalik)
type
recursiveTree
struct
{
rw
sync
.
RWMutex
// protects root
root
root
// TODO(rjeczalik): merge watcher + recursiveWatcher after #5 and #6
w
interface
{
watcher
recursiveWatcher
}
c
chan
EventInfo
}
// newRecursiveTree TODO(rjeczalik)
func
newRecursiveTree
(
w
recursiveWatcher
,
c
chan
EventInfo
)
*
recursiveTree
{
t
:=
&
recursiveTree
{
root
:
root
{
nd
:
newnode
(
""
)},
w
:
struct
{
watcher
recursiveWatcher
}{
w
.
(
watcher
),
w
},
c
:
c
,
}
go
t
.
dispatch
()
return
t
}
// dispatch TODO(rjeczalik)
func
(
t
*
recursiveTree
)
dispatch
()
{
for
ei
:=
range
t
.
c
{
dbgprintf
(
"dispatching %v on %q"
,
ei
.
Event
(),
ei
.
Path
())
go
func
(
ei
EventInfo
)
{
nd
,
ok
:=
node
{},
false
dir
,
base
:=
split
(
ei
.
Path
())
fn
:=
func
(
it
node
,
isbase
bool
)
error
{
if
isbase
{
nd
=
it
}
else
{
it
.
Watch
.
Dispatch
(
ei
,
recursive
)
}
return
nil
}
t
.
rw
.
RLock
()
defer
t
.
rw
.
RUnlock
()
// Notify recursive watchpoints found on the path.
if
err
:=
t
.
root
.
WalkPath
(
dir
,
fn
);
err
!=
nil
{
dbgprint
(
"dispatch did not reach leaf:"
,
err
)
return
}
// Notify parent watchpoint.
nd
.
Watch
.
Dispatch
(
ei
,
0
)
// If leaf watchpoint exists, notify it.
if
nd
,
ok
=
nd
.
Child
[
base
];
ok
{
nd
.
Watch
.
Dispatch
(
ei
,
0
)
}
}(
ei
)
}
}
// Watch TODO(rjeczalik)
func
(
t
*
recursiveTree
)
Watch
(
path
string
,
c
chan
<-
EventInfo
,
events
...
Event
)
error
{
if
c
==
nil
{
panic
(
"notify: Watch using nil channel"
)
}
// Expanding with empty event set is a nop.
if
len
(
events
)
==
0
{
return
nil
}
path
,
isrec
,
err
:=
cleanpath
(
path
)
if
err
!=
nil
{
return
err
}
eventset
:=
joinevents
(
events
)
if
isrec
{
eventset
|=
recursive
}
t
.
rw
.
Lock
()
defer
t
.
rw
.
Unlock
()
// case 1: cur is a child
//
// Look for parent watch which already covers the given path.
parent
:=
node
{}
self
:=
false
err
=
t
.
root
.
WalkPath
(
path
,
func
(
nd
node
,
isbase
bool
)
error
{
if
watchTotal
(
nd
)
!=
0
{
parent
=
nd
self
=
isbase
return
errSkip
}
return
nil
})
cur
:=
t
.
root
.
Add
(
path
)
// add after the walk, so it's less to traverse
if
err
==
nil
&&
parent
.
Watch
!=
nil
{
// Parent watch found. Register inactive watchpoint, so we have enough
// information to shrink the eventset on eventual Stop.
// return t.resetwatchpoint(parent, parent, c, eventset|inactive)
var
diff
eventDiff
if
self
{
diff
=
watchAdd
(
cur
,
c
,
eventset
)
}
else
{
diff
=
watchAddInactive
(
parent
,
c
,
eventset
)
}
switch
{
case
diff
==
none
:
// the parent watchpoint already covers requested subtree with its
// eventset
case
diff
[
0
]
==
0
:
// TODO(rjeczalik): cleanup this panic after implementation is stable
panic
(
"dangling watchpoint: "
+
parent
.
Name
)
default
:
if
isrec
||
watchIsRecursive
(
parent
)
{
err
=
t
.
w
.
RecursiveRewatch
(
parent
.
Name
,
parent
.
Name
,
diff
[
0
],
diff
[
1
])
}
else
{
err
=
t
.
w
.
Rewatch
(
parent
.
Name
,
diff
[
0
],
diff
[
1
])
}
if
err
!=
nil
{
watchDel
(
parent
,
c
,
diff
.
Event
())
return
err
}
watchAdd
(
cur
,
c
,
eventset
)
// TODO(rjeczalik): account top-most path for c
return
nil
}
if
!
self
{
watchAdd
(
cur
,
c
,
eventset
)
}
return
nil
}
// case 2: cur is new parent
//
// Look for children nodes, unwatch n-1 of them and rewatch the last one.
var
children
[]
node
fn
:=
func
(
nd
node
)
error
{
if
len
(
nd
.
Watch
)
==
0
{
return
nil
}
children
=
append
(
children
,
nd
)
return
errSkip
}
switch
must
(
cur
.
Walk
(
fn
));
len
(
children
)
{
case
0
:
// no child watches, cur holds a new watch
case
1
:
watchAdd
(
cur
,
c
,
eventset
)
// TODO(rjeczalik): update cache c subtree root?
watchCopy
(
children
[
0
],
cur
)
err
=
t
.
w
.
RecursiveRewatch
(
children
[
0
]
.
Name
,
cur
.
Name
,
watchTotal
(
children
[
0
]),
watchTotal
(
cur
))
if
err
!=
nil
{
// Clean inactive watchpoint. The c chan did not exist before.
cur
.
Child
[
""
]
=
node
{}
delete
(
cur
.
Watch
,
c
)
return
err
}
return
nil
default
:
watchAdd
(
cur
,
c
,
eventset
)
// Copy children inactive watchpoints to the new parent.
for
_
,
nd
:=
range
children
{
watchCopy
(
nd
,
cur
)
}
// Watch parent subtree.
if
err
=
t
.
w
.
RecursiveWatch
(
cur
.
Name
,
watchTotal
(
cur
));
err
!=
nil
{
// Clean inactive watchpoint. The c chan did not exist before.
cur
.
Child
[
""
]
=
node
{}
delete
(
cur
.
Watch
,
c
)
return
err
}
// Unwatch children subtrees.
var
e
error
for
_
,
nd
:=
range
children
{
if
watchIsRecursive
(
nd
)
{
e
=
t
.
w
.
RecursiveUnwatch
(
nd
.
Name
)
}
else
{
e
=
t
.
w
.
Unwatch
(
nd
.
Name
)
}
if
e
!=
nil
{
err
=
nonil
(
err
,
e
)
// TODO(rjeczalik): child is still watched, warn all its watchpoints
// about possible duplicate events via Error event
}
}
return
err
}
// case 3: cur is new, alone node
switch
diff
:=
watchAdd
(
cur
,
c
,
eventset
);
{
case
diff
==
none
:
// TODO(rjeczalik): cleanup this panic after implementation is stable
panic
(
"watch requested but no parent watchpoint found: "
+
cur
.
Name
)
case
diff
[
0
]
==
0
:
if
isrec
{
err
=
t
.
w
.
RecursiveWatch
(
cur
.
Name
,
diff
[
1
])
}
else
{
err
=
t
.
w
.
Watch
(
cur
.
Name
,
diff
[
1
])
}
if
err
!=
nil
{
watchDel
(
cur
,
c
,
diff
.
Event
())
return
err
}
default
:
// TODO(rjeczalik): cleanup this panic after implementation is stable
panic
(
"watch requested but no parent watchpoint found: "
+
cur
.
Name
)
}
return
nil
}
// Stop TODO(rjeczalik)
//
// TODO(rjeczalik): Split parent watchpoint - transfer watches to children
// if parent is no longer needed. This carries a risk that underlying
// watcher calls could fail - reconsider if it's worth the effort.
func
(
t
*
recursiveTree
)
Stop
(
c
chan
<-
EventInfo
)
{
var
err
error
fn
:=
func
(
nd
node
)
(
e
error
)
{
diff
:=
watchDel
(
nd
,
c
,
all
)
switch
{
case
diff
==
none
&&
watchTotal
(
nd
)
==
0
:
// TODO(rjeczalik): There's no watchpoints deeper in the tree,
// probably we should remove the nodes as well.
return
nil
case
diff
==
none
:
// Removing c from nd does not require shrinking its eventset.
case
diff
[
1
]
==
0
:
if
watchIsRecursive
(
nd
)
{
e
=
t
.
w
.
RecursiveUnwatch
(
nd
.
Name
)
}
else
{
e
=
t
.
w
.
Unwatch
(
nd
.
Name
)
}
default
:
if
watchIsRecursive
(
nd
)
{
e
=
t
.
w
.
RecursiveRewatch
(
nd
.
Name
,
nd
.
Name
,
diff
[
0
],
diff
[
1
])
}
else
{
e
=
t
.
w
.
Rewatch
(
nd
.
Name
,
diff
[
0
],
diff
[
1
])
}
}
fn
:=
func
(
nd
node
)
error
{
watchDel
(
nd
,
c
,
all
)
return
nil
}
err
=
nonil
(
err
,
e
,
nd
.
Walk
(
fn
))
// TODO(rjeczalik): if e != nil store dummy chan in nd.Watch just to
// retry un/rewatching next time and/or let the user handle the failure
// vie Error event?
return
errSkip
}
t
.
rw
.
Lock
()
e
:=
t
.
root
.
Walk
(
""
,
fn
)
// TODO(rjeczalik): use max root per c
t
.
rw
.
Unlock
()
if
e
!=
nil
{
err
=
nonil
(
err
,
e
)
}
dbgprintf
(
"Stop(%p) error: %v
\n
"
,
c
,
err
)
}
// Close TODO(rjeczalik)
func
(
t
*
recursiveTree
)
Close
()
error
{
err
:=
t
.
w
.
Close
()
close
(
t
.
c
)
return
err
}
Godeps/_workspace/src/github.com/rjeczalik/notify/util.go
0 → 100644
View file @
ef63e9af
// Copyright (c) 2014-2015 The Notify Authors. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
package
notify
import
(
"errors"
"os"
"path/filepath"
"strings"
)
const
all
=
^
Event
(
0
)
const
sep
=
string
(
os
.
PathSeparator
)
var
errDepth
=
errors
.
New
(
"exceeded allowed iteration count (circular symlink?)"
)
func
min
(
i
,
j
int
)
int
{
if
i
>
j
{
return
j
}
return
i
}
func
max
(
i
,
j
int
)
int
{
if
i
<
j
{
return
j
}
return
i
}
// must panics if err is non-nil.
func
must
(
err
error
)
{
if
err
!=
nil
{
panic
(
err
)
}
}
// nonil gives first non-nil error from the given arguments.
func
nonil
(
err
...
error
)
error
{
for
_
,
err
:=
range
err
{
if
err
!=
nil
{
return
err
}
}
return
nil
}
func
cleanpath
(
path
string
)
(
realpath
string
,
isrec
bool
,
err
error
)
{
if
strings
.
HasSuffix
(
path
,
"..."
)
{
isrec
=
true
path
=
path
[
:
len
(
path
)
-
3
]
}
if
path
,
err
=
filepath
.
Abs
(
path
);
err
!=
nil
{
return
""
,
false
,
err
}
if
path
,
err
=
canonical
(
path
);
err
!=
nil
{
return
""
,
false
,
err
}
return
path
,
isrec
,
nil
}
// canonical resolves any symlink in the given path and returns it in a clean form.
// It expects the path to be absolute. It fails to resolve circular symlinks by
// maintaining a simple iteration limit.
func
canonical
(
p
string
)
(
string
,
error
)
{
p
,
err
:=
filepath
.
Abs
(
p
)
if
err
!=
nil
{
return
""
,
err
}
for
i
,
j
,
depth
:=
1
,
0
,
1
;
i
<
len
(
p
);
i
,
depth
=
i
+
1
,
depth
+
1
{
if
depth
>
128
{
return
""
,
&
os
.
PathError
{
Op
:
"canonical"
,
Path
:
p
,
Err
:
errDepth
}
}
if
j
=
strings
.
IndexRune
(
p
[
i
:
],
'/'
);
j
==
-
1
{
j
,
i
=
i
,
len
(
p
)
}
else
{
j
,
i
=
i
,
i
+
j
}
fi
,
err
:=
os
.
Lstat
(
p
[
:
i
])
if
err
!=
nil
{
return
""
,
err
}
if
fi
.
Mode
()
&
os
.
ModeSymlink
==
os
.
ModeSymlink
{
s
,
err
:=
os
.
Readlink
(
p
[
:
i
])
if
err
!=
nil
{
return
""
,
err
}
if
filepath
.
IsAbs
(
s
)
{
p
=
"/"
+
s
+
p
[
i
:
]
}
else
{
p
=
p
[
:
j
]
+
s
+
p
[
i
:
]
}
i
=
1
// no guarantee s is canonical, start all over
}
}
return
filepath
.
Clean
(
p
),
nil
}
func
joinevents
(
events
[]
Event
)
(
e
Event
)
{
if
len
(
events
)
==
0
{
e
=
All
}
else
{
for
_
,
event
:=
range
events
{
e
|=
event
}
}
return
}
func
split
(
s
string
)
(
string
,
string
)
{
if
i
:=
lastIndexSep
(
s
);
i
!=
-
1
{
return
s
[
:
i
],
s
[
i
+
1
:
]
}
return
""
,
s
}
func
base
(
s
string
)
string
{
if
i
:=
lastIndexSep
(
s
);
i
!=
-
1
{
return
s
[
i
+
1
:
]
}
return
s
}
func
indexbase
(
root
,
name
string
)
int
{
if
n
,
m
:=
len
(
root
),
len
(
name
);
m
>=
n
&&
name
[
:
n
]
==
root
&&
(
n
==
m
||
name
[
n
]
==
os
.
PathSeparator
)
{
return
min
(
n
+
1
,
m
)
}
return
-
1
}
func
indexSep
(
s
string
)
int
{
for
i
:=
0
;
i
<
len
(
s
);
i
++
{
if
s
[
i
]
==
os
.
PathSeparator
{
return
i
}
}
return
-
1
}
func
lastIndexSep
(
s
string
)
int
{
for
i
:=
len
(
s
)
-
1
;
i
>=
0
;
i
--
{
if
s
[
i
]
==
os
.
PathSeparator
{
return
i
}
}
return
-
1
}
Godeps/_workspace/src/github.com/rjeczalik/notify/watcher.go
0 → 100644
View file @
ef63e9af
// Copyright (c) 2014-2015 The Notify Authors. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
package
notify
import
"errors"
var
(
errAlreadyWatched
=
errors
.
New
(
"path is already watched"
)
errNotWatched
=
errors
.
New
(
"path is not being watched"
)
errInvalidEventSet
=
errors
.
New
(
"invalid event set provided"
)
)
// Watcher is a intermediate interface for wrapping inotify, ReadDirChangesW,
// FSEvents, kqueue and poller implementations.
//
// The watcher implementation is expected to do its own mapping between paths and
// create watchers if underlying event notification does not support it. For
// the ease of implementation it is guaranteed that paths provided via Watch and
// Unwatch methods are absolute and clean.
type
watcher
interface
{
// Watch requests a watcher creation for the given path and given event set.
Watch
(
path
string
,
event
Event
)
error
// Unwatch requests a watcher deletion for the given path and given event set.
Unwatch
(
path
string
)
error
// Rewatch provides a functionality for modifying existing watch-points, like
// expanding its event set.
//
// Rewatch modifies existing watch-point under for the given path. It passes
// the existing event set currently registered for the given path, and the
// new, requested event set.
//
// It is guaranteed that Tree will not pass to Rewatch zero value for any
// of its arguments. If old == new and watcher can be upgraded to
// recursiveWatcher interface, a watch for the corresponding path is expected
// to be changed from recursive to the non-recursive one.
Rewatch
(
path
string
,
old
,
new
Event
)
error
// Close unwatches all paths that are registered. When Close returns, it
// is expected it will report no more events.
Close
()
error
}
// RecursiveWatcher is an interface for a Watcher for those OS, which do support
// recursive watching over directories.
type
recursiveWatcher
interface
{
RecursiveWatch
(
path
string
,
event
Event
)
error
// RecursiveUnwatch removes a recursive watch-point given by the path. For
// native recursive implementation there is no difference in functionality
// between Unwatch and RecursiveUnwatch, however for those platforms, that
// requires emulation for recursive watch-points, the implementation differs.
RecursiveUnwatch
(
path
string
)
error
// RecursiveRewatcher provides a functionality for modifying and/or relocating
// existing recursive watch-points.
//
// To relocate a watch-point means to unwatch oldpath and set a watch-point on
// newpath.
//
// To modify a watch-point means either to expand or shrink its event set.
//
// Tree can want to either relocate, modify or relocate and modify a watch-point
// via single RecursiveRewatch call.
//
// If oldpath == newpath, the watch-point is expected to change its event set value
// from oldevent to newevent.
//
// If oldevent == newevent, the watch-point is expected to relocate from oldpath
// to the newpath.
//
// If oldpath != newpath and oldevent != newevent, the watch-point is expected
// to relocate from oldpath to the newpath first and then change its event set
// value from oldevent to the newevent. In other words the end result must be
// a watch-point set on newpath with newevent value of its event set.
//
// It is guaranteed that Tree will not pass to RecurisveRewatcha zero value
// for any of its arguments. If oldpath == newpath and oldevent == newevent,
// a watch for the corresponding path is expected to be changed for
// non-recursive to the recursive one.
RecursiveRewatch
(
oldpath
,
newpath
string
,
oldevent
,
newevent
Event
)
error
}
Godeps/_workspace/src/github.com/rjeczalik/notify/watcher_fen.go
0 → 100644
View file @
ef63e9af
// Copyright (c) 2014-2015 The Notify Authors. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
// +build solaris
package
notify
import
(
"fmt"
"os"
"syscall"
)
// newTrigger returns implementation of trigger.
func
newTrigger
(
pthLkp
map
[
string
]
*
watched
)
trigger
{
return
&
fen
{
pthLkp
:
pthLkp
,
cf
:
newCfen
(),
}
}
// fen is a structure implementing trigger for FEN.
type
fen
struct
{
// p is a FEN port identifier
p
int
// pthLkp is a structure mapping monitored files/dir with data about them,
// shared with parent trg structure
pthLkp
map
[
string
]
*
watched
// cf wraps C operations for FEN
cf
cfen
}
// watched is a data structure representing watched file/directory.
type
watched
struct
{
// p is a path to watched file/directory
p
string
// fi provides information about watched file/dir
fi
os
.
FileInfo
// eDir represents events watched directly
eDir
Event
// eNonDir represents events watched indirectly
eNonDir
Event
}
// Stop implements trigger.
func
(
f
*
fen
)
Stop
()
error
{
return
f
.
cf
.
port_alert
(
f
.
p
)
}
// Close implements trigger.
func
(
f
*
fen
)
Close
()
(
err
error
)
{
return
syscall
.
Close
(
f
.
p
)
}
// NewWatched implements trigger.
func
(
*
fen
)
NewWatched
(
p
string
,
fi
os
.
FileInfo
)
(
*
watched
,
error
)
{
return
&
watched
{
p
:
p
,
fi
:
fi
},
nil
}
// Record implements trigger.
func
(
f
*
fen
)
Record
(
w
*
watched
)
{
f
.
pthLkp
[
w
.
p
]
=
w
}
// Del implements trigger.
func
(
f
*
fen
)
Del
(
w
*
watched
)
{
delete
(
f
.
pthLkp
,
w
.
p
)
}
func
inter2pe
(
n
interface
{})
PortEvent
{
pe
,
ok
:=
n
.
(
PortEvent
)
if
!
ok
{
panic
(
fmt
.
Sprintf
(
"fen: type should be PortEvent, %T instead"
,
n
))
}
return
pe
}
// Watched implements trigger.
func
(
f
*
fen
)
Watched
(
n
interface
{})
(
*
watched
,
int64
,
error
)
{
pe
:=
inter2pe
(
n
)
fo
,
ok
:=
pe
.
PortevObject
.
(
*
FileObj
)
if
!
ok
||
fo
==
nil
{
panic
(
fmt
.
Sprintf
(
"fen: type should be *FileObj, %T instead"
,
fo
))
}
w
,
ok
:=
f
.
pthLkp
[
fo
.
Name
]
if
!
ok
{
return
nil
,
0
,
errNotWatched
}
return
w
,
int64
(
pe
.
PortevEvents
),
nil
}
// init initializes FEN.
func
(
f
*
fen
)
Init
()
(
err
error
)
{
f
.
p
,
err
=
f
.
cf
.
port_create
()
return
}
func
fi2fo
(
fi
os
.
FileInfo
,
p
string
)
FileObj
{
st
,
ok
:=
fi
.
Sys
()
.
(
*
syscall
.
Stat_t
)
if
!
ok
{
panic
(
fmt
.
Sprintf
(
"fen: type should be *syscall.Stat_t, %T instead"
,
st
))
}
return
FileObj
{
Name
:
p
,
Atim
:
st
.
Atim
,
Mtim
:
st
.
Mtim
,
Ctim
:
st
.
Ctim
}
}
// Unwatch implements trigger.
func
(
f
*
fen
)
Unwatch
(
w
*
watched
)
error
{
return
f
.
cf
.
port_dissociate
(
f
.
p
,
FileObj
{
Name
:
w
.
p
})
}
// Watch implements trigger.
func
(
f
*
fen
)
Watch
(
fi
os
.
FileInfo
,
w
*
watched
,
e
int64
)
error
{
return
f
.
cf
.
port_associate
(
f
.
p
,
fi2fo
(
fi
,
w
.
p
),
int
(
e
))
}
// Wait implements trigger.
func
(
f
*
fen
)
Wait
()
(
interface
{},
error
)
{
var
(
pe
PortEvent
err
error
)
err
=
f
.
cf
.
port_get
(
f
.
p
,
&
pe
)
return
pe
,
err
}
// IsStop implements trigger.
func
(
f
*
fen
)
IsStop
(
n
interface
{},
err
error
)
bool
{
return
err
==
syscall
.
EBADF
||
inter2pe
(
n
)
.
PortevSource
==
srcAlert
}
func
init
()
{
encode
=
func
(
e
Event
)
(
o
int64
)
{
// Create event is not supported by FEN. Instead FileModified event will
// be registered. If this event will be reported on dir which is to be
// monitored for Create, dir will be rescanned and Create events will
// be generated and returned for new files. In case of files,
// if not requested FileModified event is reported, it will be ignored.
if
e
&
Create
!=
0
{
o
=
(
o
&^
int64
(
Create
))
|
int64
(
FileModified
)
}
if
e
&
Write
!=
0
{
o
=
(
o
&^
int64
(
Write
))
|
int64
(
FileModified
)
}
// Following events are 'exception events' and as such cannot be requested
// explicitly for monitoring or filtered out. If the will be reported
// by FEN and not subscribed with by user, they will be filtered out by
// watcher's logic.
o
&=
int64
(
^
Rename
&
^
Remove
&^
FileDelete
&^
FileRenameTo
&^
FileRenameFrom
&^
Unmounted
&^
MountedOver
)
return
}
nat2not
=
map
[
Event
]
Event
{
FileModified
:
Write
,
FileRenameFrom
:
Rename
,
FileDelete
:
Remove
,
FileAccess
:
Event
(
0
),
FileAttrib
:
Event
(
0
),
FileRenameTo
:
Event
(
0
),
FileTrunc
:
Event
(
0
),
FileNoFollow
:
Event
(
0
),
Unmounted
:
Event
(
0
),
MountedOver
:
Event
(
0
),
}
not2nat
=
map
[
Event
]
Event
{
Write
:
FileModified
,
Rename
:
FileRenameFrom
,
Remove
:
FileDelete
,
}
}
Godeps/_workspace/src/github.com/rjeczalik/notify/watcher_fen_cgo.go
0 → 100644
View file @
ef63e9af
// Copyright (c) 2014-2015 The Notify Authors. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
// +build solaris
package
notify
// #include <port.h>
// #include <stdio.h>
// #include <stdlib.h>
// struct file_obj* newFo() { return (struct file_obj*) malloc(sizeof(struct file_obj)); }
// port_event_t* newPe() { return (port_event_t*) malloc(sizeof(port_event_t)); }
// uintptr_t conv(struct file_obj* fo) { return (uintptr_t) fo; }
// struct file_obj* dconv(uintptr_t fo) { return (struct file_obj*) fo; }
import
"C"
import
(
"syscall"
"unsafe"
)
const
(
fileAccess
=
Event
(
C
.
FILE_ACCESS
)
fileModified
=
Event
(
C
.
FILE_MODIFIED
)
fileAttrib
=
Event
(
C
.
FILE_ATTRIB
)
fileDelete
=
Event
(
C
.
FILE_DELETE
)
fileRenameTo
=
Event
(
C
.
FILE_RENAME_TO
)
fileRenameFrom
=
Event
(
C
.
FILE_RENAME_FROM
)
fileTrunc
=
Event
(
C
.
FILE_TRUNC
)
fileNoFollow
=
Event
(
C
.
FILE_NOFOLLOW
)
unmounted
=
Event
(
C
.
UNMOUNTED
)
mountedOver
=
Event
(
C
.
MOUNTEDOVER
)
)
// PortEvent is a notify's equivalent of port_event_t.
type
PortEvent
struct
{
PortevEvents
int
// PortevEvents is an equivalent of portev_events.
PortevSource
uint8
// PortevSource is an equivalent of portev_source.
PortevPad
uint8
// Portevpad is an equivalent of portev_pad.
PortevObject
interface
{}
// PortevObject is an equivalent of portev_object.
PortevUser
uintptr
// PortevUser is an equivalent of portev_user.
}
// FileObj is a notify's equivalent of file_obj.
type
FileObj
struct
{
Atim
syscall
.
Timespec
// Atim is an equivalent of fo_atime.
Mtim
syscall
.
Timespec
// Mtim is an equivalent of fo_mtime.
Ctim
syscall
.
Timespec
// Ctim is an equivalent of fo_ctime.
Pad
[
3
]
uintptr
// Pad is an equivalent of fo_pad.
Name
string
// Name is an equivalent of fo_name.
}
type
cfen
struct
{
p2pe
map
[
string
]
*
C
.
port_event_t
p2fo
map
[
string
]
*
C
.
struct_file_obj
}
func
newCfen
()
cfen
{
return
cfen
{
p2pe
:
make
(
map
[
string
]
*
C
.
port_event_t
),
p2fo
:
make
(
map
[
string
]
*
C
.
struct_file_obj
),
}
}
func
unix2C
(
sec
int64
,
nsec
int64
)
(
C
.
time_t
,
C
.
long
)
{
return
C
.
time_t
(
sec
),
C
.
long
(
nsec
)
}
func
(
c
*
cfen
)
port_associate
(
p
int
,
fo
FileObj
,
e
int
)
(
err
error
)
{
cfo
:=
C
.
newFo
()
cfo
.
fo_atime
.
tv_sec
,
cfo
.
fo_atime
.
tv_nsec
=
unix2C
(
fo
.
Atim
.
Unix
())
cfo
.
fo_mtime
.
tv_sec
,
cfo
.
fo_mtime
.
tv_nsec
=
unix2C
(
fo
.
Mtim
.
Unix
())
cfo
.
fo_ctime
.
tv_sec
,
cfo
.
fo_ctime
.
tv_nsec
=
unix2C
(
fo
.
Ctim
.
Unix
())
cfo
.
fo_name
=
C
.
CString
(
fo
.
Name
)
c
.
p2fo
[
fo
.
Name
]
=
cfo
_
,
err
=
C
.
port_associate
(
C
.
int
(
p
),
srcFile
,
C
.
conv
(
cfo
),
C
.
int
(
e
),
nil
)
return
}
func
(
c
*
cfen
)
port_dissociate
(
port
int
,
fo
FileObj
)
(
err
error
)
{
cfo
,
ok
:=
c
.
p2fo
[
fo
.
Name
]
if
!
ok
{
return
errNotWatched
}
_
,
err
=
C
.
port_dissociate
(
C
.
int
(
port
),
srcFile
,
C
.
conv
(
cfo
))
C
.
free
(
unsafe
.
Pointer
(
cfo
.
fo_name
))
C
.
free
(
unsafe
.
Pointer
(
cfo
))
delete
(
c
.
p2fo
,
fo
.
Name
)
return
}
const
srcAlert
=
C
.
PORT_SOURCE_ALERT
const
srcFile
=
C
.
PORT_SOURCE_FILE
const
alertSet
=
C
.
PORT_ALERT_SET
func
cfo2fo
(
cfo
*
C
.
struct_file_obj
)
*
FileObj
{
// Currently remaining attributes are not used.
if
cfo
==
nil
{
return
nil
}
var
fo
FileObj
fo
.
Name
=
C
.
GoString
(
cfo
.
fo_name
)
return
&
fo
}
func
(
c
*
cfen
)
port_get
(
port
int
,
pe
*
PortEvent
)
(
err
error
)
{
cpe
:=
C
.
newPe
()
if
_
,
err
=
C
.
port_get
(
C
.
int
(
port
),
cpe
,
nil
);
err
!=
nil
{
C
.
free
(
unsafe
.
Pointer
(
cpe
))
return
}
pe
.
PortevEvents
,
pe
.
PortevSource
,
pe
.
PortevPad
=
int
(
cpe
.
portev_events
),
uint8
(
cpe
.
portev_source
),
uint8
(
cpe
.
portev_pad
)
pe
.
PortevObject
=
cfo2fo
(
C
.
dconv
(
cpe
.
portev_object
))
pe
.
PortevUser
=
uintptr
(
cpe
.
portev_user
)
C
.
free
(
unsafe
.
Pointer
(
cpe
))
return
}
func
(
c
*
cfen
)
port_create
()
(
int
,
error
)
{
p
,
err
:=
C
.
port_create
()
return
int
(
p
),
err
}
func
(
c
*
cfen
)
port_alert
(
p
int
)
(
err
error
)
{
_
,
err
=
C
.
port_alert
(
C
.
int
(
p
),
alertSet
,
C
.
int
(
666
),
nil
)
return
}
func
(
c
*
cfen
)
free
()
{
for
i
:=
range
c
.
p2fo
{
C
.
free
(
unsafe
.
Pointer
(
c
.
p2fo
[
i
]
.
fo_name
))
C
.
free
(
unsafe
.
Pointer
(
c
.
p2fo
[
i
]))
}
for
i
:=
range
c
.
p2pe
{
C
.
free
(
unsafe
.
Pointer
(
c
.
p2pe
[
i
]))
}
c
.
p2fo
=
make
(
map
[
string
]
*
C
.
struct_file_obj
)
c
.
p2pe
=
make
(
map
[
string
]
*
C
.
port_event_t
)
}
Godeps/_workspace/src/github.com/rjeczalik/notify/watcher_fsevents.go
0 → 100644
View file @
ef63e9af
// Copyright (c) 2014-2015 The Notify Authors. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
// +build darwin,!kqueue
package
notify
import
(
"errors"
"strings"
"sync/atomic"
)
// TODO(rjeczalik): get rid of calls to canonical, it's tree responsibility
const
(
failure
=
uint32
(
FSEventsMustScanSubDirs
|
FSEventsUserDropped
|
FSEventsKernelDropped
)
filter
=
uint32
(
FSEventsCreated
|
FSEventsRemoved
|
FSEventsRenamed
|
FSEventsModified
|
FSEventsInodeMetaMod
)
)
// FSEvent represents single file event. It is created out of values passed by
// FSEvents to FSEventStreamCallback function.
type
FSEvent
struct
{
Path
string
// real path of the file or directory
ID
uint64
// ID of the event (FSEventStreamEventId)
Flags
uint32
// joint FSEvents* flags (FSEventStreamEventFlags)
}
// splitflags separates event flags from single set into slice of flags.
func
splitflags
(
set
uint32
)
(
e
[]
uint32
)
{
for
i
:=
uint32
(
1
);
set
!=
0
;
i
,
set
=
i
<<
1
,
set
>>
1
{
if
(
set
&
1
)
!=
0
{
e
=
append
(
e
,
i
)
}
}
return
}
// watch represents a filesystem watchpoint. It is a higher level abstraction
// over FSEvents' stream, which implements filtering of file events based
// on path and event set. It emulates non-recursive watch-point by filtering out
// events which paths are more than 1 level deeper than the watched path.
type
watch
struct
{
// prev stores last event set per path in order to filter out old flags
// for new events, which appratenly FSEvents likes to retain. It's a disgusting
// hack, it should be researched how to get rid of it.
prev
map
[
string
]
uint32
c
chan
<-
EventInfo
stream
*
stream
path
string
events
uint32
isrec
int32
flushed
bool
}
// Example format:
//
// ~ $ (trigger command) # (event set) -> (effective event set)
//
// Heuristics:
//
// 1. Create event is removed when it was present in previous event set.
// Example:
//
// ~ $ echo > file # Create|Write -> Create|Write
// ~ $ echo > file # Create|Write|InodeMetaMod -> Write|InodeMetaMod
//
// 2. Remove event is removed if it was present in previouse event set.
// Example:
//
// ~ $ touch file # Create -> Create
// ~ $ rm file # Create|Remove -> Remove
// ~ $ touch file # Create|Remove -> Create
//
// 3. Write event is removed if not followed by InodeMetaMod on existing
// file. Example:
//
// ~ $ echo > file # Create|Write -> Create|Write
// ~ $ chmod +x file # Create|Write|ChangeOwner -> ChangeOwner
//
// 4. Write&InodeMetaMod is removed when effective event set contain Remove event.
// Example:
//
// ~ $ echo > file # Write|InodeMetaMod -> Write|InodeMetaMod
// ~ $ rm file # Remove|Write|InodeMetaMod -> Remove
//
func
(
w
*
watch
)
strip
(
base
string
,
set
uint32
)
uint32
{
const
(
write
=
FSEventsModified
|
FSEventsInodeMetaMod
both
=
FSEventsCreated
|
FSEventsRemoved
)
switch
w
.
prev
[
base
]
{
case
FSEventsCreated
:
set
&^=
FSEventsCreated
if
set
&
FSEventsRemoved
!=
0
{
w
.
prev
[
base
]
=
FSEventsRemoved
set
&^=
write
}
case
FSEventsRemoved
:
set
&^=
FSEventsRemoved
if
set
&
FSEventsCreated
!=
0
{
w
.
prev
[
base
]
=
FSEventsCreated
}
default
:
switch
set
&
both
{
case
FSEventsCreated
:
w
.
prev
[
base
]
=
FSEventsCreated
case
FSEventsRemoved
:
w
.
prev
[
base
]
=
FSEventsRemoved
set
&^=
write
}
}
dbgprintf
(
"split()=%v
\n
"
,
Event
(
set
))
return
set
}
// Dispatch is a stream function which forwards given file events for the watched
// path to underlying FileInfo channel.
func
(
w
*
watch
)
Dispatch
(
ev
[]
FSEvent
)
{
events
:=
atomic
.
LoadUint32
(
&
w
.
events
)
isrec
:=
(
atomic
.
LoadInt32
(
&
w
.
isrec
)
==
1
)
for
i
:=
range
ev
{
if
ev
[
i
]
.
Flags
&
FSEventsHistoryDone
!=
0
{
w
.
flushed
=
true
continue
}
if
!
w
.
flushed
{
continue
}
dbgprintf
(
"%v (0x%x) (%s, i=%d, ID=%d, len=%d)
\n
"
,
Event
(
ev
[
i
]
.
Flags
),
ev
[
i
]
.
Flags
,
ev
[
i
]
.
Path
,
i
,
ev
[
i
]
.
ID
,
len
(
ev
))
if
ev
[
i
]
.
Flags
&
failure
!=
0
{
// TODO(rjeczalik): missing error handling
panic
(
"unhandled error: "
+
Event
(
ev
[
i
]
.
Flags
)
.
String
())
}
if
!
strings
.
HasPrefix
(
ev
[
i
]
.
Path
,
w
.
path
)
{
continue
}
n
:=
len
(
w
.
path
)
base
:=
""
if
len
(
ev
[
i
]
.
Path
)
>
n
{
if
ev
[
i
]
.
Path
[
n
]
!=
'/'
{
continue
}
base
=
ev
[
i
]
.
Path
[
n
+
1
:
]
if
!
isrec
&&
strings
.
IndexByte
(
base
,
'/'
)
!=
-
1
{
continue
}
}
// TODO(rjeczalik): get diff only from filtered events?
e
:=
w
.
strip
(
string
(
base
),
ev
[
i
]
.
Flags
)
&
events
if
e
==
0
{
continue
}
for
_
,
e
:=
range
splitflags
(
e
)
{
dbgprintf
(
"%d: single event: %v"
,
ev
[
i
]
.
ID
,
Event
(
e
))
w
.
c
<-
&
event
{
fse
:
ev
[
i
],
event
:
Event
(
e
),
}
}
}
}
// Stop closes underlying FSEvents stream and stops dispatching events.
func
(
w
*
watch
)
Stop
()
{
w
.
stream
.
Stop
()
// TODO(rjeczalik): make (*stream).Stop flush synchronously undelivered events,
// so the following hack can be removed. It should flush all the streams
// concurrently as we care not to block too much here.
atomic
.
StoreUint32
(
&
w
.
events
,
0
)
atomic
.
StoreInt32
(
&
w
.
isrec
,
0
)
}
// fsevents implements Watcher and RecursiveWatcher interfaces backed by FSEvents
// framework.
type
fsevents
struct
{
watches
map
[
string
]
*
watch
c
chan
<-
EventInfo
}
func
newWatcher
(
c
chan
<-
EventInfo
)
watcher
{
return
&
fsevents
{
watches
:
make
(
map
[
string
]
*
watch
),
c
:
c
,
}
}
func
(
fse
*
fsevents
)
watch
(
path
string
,
event
Event
,
isrec
int32
)
(
err
error
)
{
if
path
,
err
=
canonical
(
path
);
err
!=
nil
{
return
err
}
if
_
,
ok
:=
fse
.
watches
[
path
];
ok
{
return
errAlreadyWatched
}
w
:=
&
watch
{
prev
:
make
(
map
[
string
]
uint32
),
c
:
fse
.
c
,
path
:
path
,
events
:
uint32
(
event
),
isrec
:
isrec
,
}
w
.
stream
=
newStream
(
path
,
w
.
Dispatch
)
if
err
=
w
.
stream
.
Start
();
err
!=
nil
{
return
err
}
fse
.
watches
[
path
]
=
w
return
nil
}
func
(
fse
*
fsevents
)
unwatch
(
path
string
)
(
err
error
)
{
if
path
,
err
=
canonical
(
path
);
err
!=
nil
{
return
}
w
,
ok
:=
fse
.
watches
[
path
]
if
!
ok
{
return
errNotWatched
}
w
.
stream
.
Stop
()
delete
(
fse
.
watches
,
path
)
return
nil
}
// Watch implements Watcher interface. It fails with non-nil error when setting
// the watch-point by FSEvents fails or with errAlreadyWatched error when
// the given path is already watched.
func
(
fse
*
fsevents
)
Watch
(
path
string
,
event
Event
)
error
{
return
fse
.
watch
(
path
,
event
,
0
)
}
// Unwatch implements Watcher interface. It fails with errNotWatched when
// the given path is not being watched.
func
(
fse
*
fsevents
)
Unwatch
(
path
string
)
error
{
return
fse
.
unwatch
(
path
)
}
// Rewatch implements Watcher interface. It fails with errNotWatched when
// the given path is not being watched or with errInvalidEventSet when oldevent
// does not match event set the watch-point currently holds.
func
(
fse
*
fsevents
)
Rewatch
(
path
string
,
oldevent
,
newevent
Event
)
error
{
w
,
ok
:=
fse
.
watches
[
path
]
if
!
ok
{
return
errNotWatched
}
if
!
atomic
.
CompareAndSwapUint32
(
&
w
.
events
,
uint32
(
oldevent
),
uint32
(
newevent
))
{
return
errInvalidEventSet
}
atomic
.
StoreInt32
(
&
w
.
isrec
,
0
)
return
nil
}
// RecursiveWatch implements RecursiveWatcher interface. It fails with non-nil
// error when setting the watch-point by FSEvents fails or with errAlreadyWatched
// error when the given path is already watched.
func
(
fse
*
fsevents
)
RecursiveWatch
(
path
string
,
event
Event
)
error
{
return
fse
.
watch
(
path
,
event
,
1
)
}
// RecursiveUnwatch implements RecursiveWatcher interface. It fails with
// errNotWatched when the given path is not being watched.
//
// TODO(rjeczalik): fail if w.isrec == 0?
func
(
fse
*
fsevents
)
RecursiveUnwatch
(
path
string
)
error
{
return
fse
.
unwatch
(
path
)
}
// RecrusiveRewatch implements RecursiveWatcher interface. It fails:
//
// * with errNotWatched when the given path is not being watched
// * with errInvalidEventSet when oldevent does not match the current event set
// * with errAlreadyWatched when watch-point given by the oldpath was meant to
// be relocated to newpath, but the newpath is already watched
// * a non-nil error when setting the watch-point with FSEvents fails
//
// TODO(rjeczalik): Improve handling of watch-point relocation? See two TODOs
// that follows.
func
(
fse
*
fsevents
)
RecursiveRewatch
(
oldpath
,
newpath
string
,
oldevent
,
newevent
Event
)
error
{
switch
[
2
]
bool
{
oldpath
==
newpath
,
oldevent
==
newevent
}
{
case
[
2
]
bool
{
true
,
true
}
:
w
,
ok
:=
fse
.
watches
[
oldpath
]
if
!
ok
{
return
errNotWatched
}
atomic
.
StoreInt32
(
&
w
.
isrec
,
1
)
return
nil
case
[
2
]
bool
{
true
,
false
}
:
w
,
ok
:=
fse
.
watches
[
oldpath
]
if
!
ok
{
return
errNotWatched
}
if
!
atomic
.
CompareAndSwapUint32
(
&
w
.
events
,
uint32
(
oldevent
),
uint32
(
newevent
))
{
return
errors
.
New
(
"invalid event state diff"
)
}
atomic
.
StoreInt32
(
&
w
.
isrec
,
1
)
return
nil
default
:
// TODO(rjeczalik): rewatch newpath only if exists?
// TODO(rjeczalik): migrate w.prev to new watch?
if
_
,
ok
:=
fse
.
watches
[
newpath
];
ok
{
return
errAlreadyWatched
}
if
err
:=
fse
.
Unwatch
(
oldpath
);
err
!=
nil
{
return
err
}
// TODO(rjeczalik): revert unwatch if watch fails?
return
fse
.
watch
(
newpath
,
newevent
,
1
)
}
}
// Close unwatches all watch-points.
func
(
fse
*
fsevents
)
Close
()
error
{
for
_
,
w
:=
range
fse
.
watches
{
w
.
Stop
()
}
fse
.
watches
=
nil
return
nil
}
Godeps/_workspace/src/github.com/rjeczalik/notify/watcher_fsevents_cgo.go
0 → 100644
View file @
ef63e9af
// Copyright (c) 2014-2015 The Notify Authors. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
// +build darwin,!kqueue
package
notify
/*
#include <CoreServices/CoreServices.h>
typedef void (*CFRunLoopPerformCallBack)(void*);
void gosource(void *);
void gostream(uintptr_t, uintptr_t, size_t, uintptr_t, uintptr_t, uintptr_t);
static FSEventStreamRef EventStreamCreate(FSEventStreamContext * context, uintptr_t info, CFArrayRef paths, FSEventStreamEventId since, CFTimeInterval latency, FSEventStreamCreateFlags flags) {
context->info = (void*) info;
return FSEventStreamCreate(NULL, (FSEventStreamCallback) gostream, context, paths, since, latency, flags);
}
#cgo LDFLAGS: -framework CoreServices
*/
import
"C"
import
(
"errors"
"os"
"sync"
"sync/atomic"
"time"
"unsafe"
)
var
nilstream
C
.
FSEventStreamRef
// Default arguments for FSEventStreamCreate function.
var
(
latency
C
.
CFTimeInterval
flags
=
C
.
FSEventStreamCreateFlags
(
C
.
kFSEventStreamCreateFlagFileEvents
|
C
.
kFSEventStreamCreateFlagNoDefer
)
since
=
uint64
(
C
.
FSEventsGetCurrentEventId
())
)
var
runloop
C
.
CFRunLoopRef
// global runloop which all streams are registered with
var
wg
sync
.
WaitGroup
// used to wait until the runloop starts
// source is used for synchronization purposes - it signals when runloop has
// started and is ready via the wg. It also serves purpose of a dummy source,
// thanks to it the runloop does not return as it also has at least one source
// registered.
var
source
=
C
.
CFRunLoopSourceCreate
(
nil
,
0
,
&
C
.
CFRunLoopSourceContext
{
perform
:
(
C
.
CFRunLoopPerformCallBack
)(
C
.
gosource
),
})
// Errors returned when FSEvents functions fail.
var
(
errCreate
=
os
.
NewSyscallError
(
"FSEventStreamCreate"
,
errors
.
New
(
"NULL"
))
errStart
=
os
.
NewSyscallError
(
"FSEventStreamStart"
,
errors
.
New
(
"false"
))
)
// initializes the global runloop and ensures any created stream awaits its
// readiness.
func
init
()
{
wg
.
Add
(
1
)
go
func
()
{
runloop
=
C
.
CFRunLoopGetCurrent
()
C
.
CFRunLoopAddSource
(
runloop
,
source
,
C
.
kCFRunLoopDefaultMode
)
C
.
CFRunLoopRun
()
panic
(
"runloop has just unexpectedly stopped"
)
}()
C
.
CFRunLoopSourceSignal
(
source
)
}
//export gosource
func
gosource
(
unsafe
.
Pointer
)
{
time
.
Sleep
(
time
.
Second
)
wg
.
Done
()
}
//export gostream
func
gostream
(
_
,
info
uintptr
,
n
C
.
size_t
,
paths
,
flags
,
ids
uintptr
)
{
const
(
offchar
=
unsafe
.
Sizeof
((
*
C
.
char
)(
nil
))
offflag
=
unsafe
.
Sizeof
(
C
.
FSEventStreamEventFlags
(
0
))
offid
=
unsafe
.
Sizeof
(
C
.
FSEventStreamEventId
(
0
))
)
if
n
==
0
{
return
}
ev
:=
make
([]
FSEvent
,
0
,
int
(
n
))
for
i
:=
uintptr
(
0
);
i
<
uintptr
(
n
);
i
++
{
switch
flags
:=
*
(
*
uint32
)(
unsafe
.
Pointer
((
flags
+
i
*
offflag
)));
{
case
flags
&
uint32
(
FSEventsEventIdsWrapped
)
!=
0
:
atomic
.
StoreUint64
(
&
since
,
uint64
(
C
.
FSEventsGetCurrentEventId
()))
default
:
ev
=
append
(
ev
,
FSEvent
{
Path
:
C
.
GoString
(
*
(
**
C
.
char
)(
unsafe
.
Pointer
(
paths
+
i
*
offchar
))),
Flags
:
flags
,
ID
:
*
(
*
uint64
)(
unsafe
.
Pointer
(
ids
+
i
*
offid
)),
})
}
}
streamFuncs
.
get
(
info
)(
ev
)
}
// StreamFunc is a callback called when stream receives file events.
type
streamFunc
func
([]
FSEvent
)
var
streamFuncs
=
streamFuncRegistry
{
m
:
map
[
uintptr
]
streamFunc
{}}
type
streamFuncRegistry
struct
{
mu
sync
.
Mutex
m
map
[
uintptr
]
streamFunc
i
uintptr
}
func
(
r
*
streamFuncRegistry
)
get
(
id
uintptr
)
streamFunc
{
r
.
mu
.
Lock
()
defer
r
.
mu
.
Unlock
()
return
r
.
m
[
id
]
}
func
(
r
*
streamFuncRegistry
)
add
(
fn
streamFunc
)
uintptr
{
r
.
mu
.
Lock
()
defer
r
.
mu
.
Unlock
()
r
.
i
++
r
.
m
[
r
.
i
]
=
fn
return
r
.
i
}
func
(
r
*
streamFuncRegistry
)
delete
(
id
uintptr
)
{
r
.
mu
.
Lock
()
defer
r
.
mu
.
Unlock
()
delete
(
r
.
m
,
id
)
}
// Stream represents single watch-point which listens for events scheduled by
// the global runloop.
type
stream
struct
{
path
string
ref
C
.
FSEventStreamRef
info
uintptr
}
// NewStream creates a stream for given path, listening for file events and
// calling fn upon receving any.
func
newStream
(
path
string
,
fn
streamFunc
)
*
stream
{
return
&
stream
{
path
:
path
,
info
:
streamFuncs
.
add
(
fn
),
}
}
// Start creates a FSEventStream for the given path and schedules it with
// global runloop. It's a nop if the stream was already started.
func
(
s
*
stream
)
Start
()
error
{
if
s
.
ref
!=
nilstream
{
return
nil
}
wg
.
Wait
()
p
:=
C
.
CFStringCreateWithCStringNoCopy
(
nil
,
C
.
CString
(
s
.
path
),
C
.
kCFStringEncodingUTF8
,
nil
)
path
:=
C
.
CFArrayCreate
(
nil
,
(
*
unsafe
.
Pointer
)(
unsafe
.
Pointer
(
&
p
)),
1
,
nil
)
ctx
:=
C
.
FSEventStreamContext
{}
ref
:=
C
.
EventStreamCreate
(
&
ctx
,
C
.
uintptr_t
(
s
.
info
),
path
,
C
.
FSEventStreamEventId
(
atomic
.
LoadUint64
(
&
since
)),
latency
,
flags
)
if
ref
==
nilstream
{
return
errCreate
}
C
.
FSEventStreamScheduleWithRunLoop
(
ref
,
runloop
,
C
.
kCFRunLoopDefaultMode
)
if
C
.
FSEventStreamStart
(
ref
)
==
C
.
Boolean
(
0
)
{
C
.
FSEventStreamInvalidate
(
ref
)
return
errStart
}
C
.
CFRunLoopWakeUp
(
runloop
)
s
.
ref
=
ref
return
nil
}
// Stop stops underlying FSEventStream and unregisters it from global runloop.
func
(
s
*
stream
)
Stop
()
{
if
s
.
ref
==
nilstream
{
return
}
wg
.
Wait
()
C
.
FSEventStreamStop
(
s
.
ref
)
C
.
FSEventStreamInvalidate
(
s
.
ref
)
C
.
CFRunLoopWakeUp
(
runloop
)
s
.
ref
=
nilstream
streamFuncs
.
delete
(
s
.
info
)
}
Godeps/_workspace/src/github.com/rjeczalik/notify/watcher_inotify.go
0 → 100644
View file @
ef63e9af
This diff is collapsed.
Click to expand it.
Godeps/_workspace/src/github.com/rjeczalik/notify/watcher_kqueue.go
0 → 100644
View file @
ef63e9af
// Copyright (c) 2014-2015 The Notify Authors. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
// +build darwin,kqueue dragonfly freebsd netbsd openbsd
package
notify
import
(
"fmt"
"os"
"syscall"
)
// newTrigger returns implementation of trigger.
func
newTrigger
(
pthLkp
map
[
string
]
*
watched
)
trigger
{
return
&
kq
{
pthLkp
:
pthLkp
,
idLkp
:
make
(
map
[
int
]
*
watched
),
}
}
// kq is a structure implementing trigger for kqueue.
type
kq
struct
{
// fd is a kqueue file descriptor
fd
int
// pipefds are file descriptors used to stop `Kevent` call.
pipefds
[
2
]
int
// idLkp is a data structure mapping file descriptors with data about watching
// represented by them files/directories.
idLkp
map
[
int
]
*
watched
// pthLkp is a structure mapping monitored files/dir with data about them,
// shared with parent trg structure
pthLkp
map
[
string
]
*
watched
}
// watched is a data structure representing watched file/directory.
type
watched
struct
{
// p is a path to watched file/directory.
p
string
// fd is a file descriptor for watched file/directory.
fd
int
// fi provides information about watched file/dir.
fi
os
.
FileInfo
// eDir represents events watched directly.
eDir
Event
// eNonDir represents events watched indirectly.
eNonDir
Event
}
// Stop implements trigger.
func
(
k
*
kq
)
Stop
()
(
err
error
)
{
// trigger event used to interrupt Kevent call.
_
,
err
=
syscall
.
Write
(
k
.
pipefds
[
1
],
[]
byte
{
0x00
})
return
}
// Close implements trigger.
func
(
k
*
kq
)
Close
()
error
{
return
syscall
.
Close
(
k
.
fd
)
}
// NewWatched implements trigger.
func
(
*
kq
)
NewWatched
(
p
string
,
fi
os
.
FileInfo
)
(
*
watched
,
error
)
{
fd
,
err
:=
syscall
.
Open
(
p
,
syscall
.
O_NONBLOCK
|
syscall
.
O_RDONLY
,
0
)
if
err
!=
nil
{
return
nil
,
err
}
return
&
watched
{
fd
:
fd
,
p
:
p
,
fi
:
fi
},
nil
}
// Record implements trigger.
func
(
k
*
kq
)
Record
(
w
*
watched
)
{
k
.
idLkp
[
w
.
fd
],
k
.
pthLkp
[
w
.
p
]
=
w
,
w
}
// Del implements trigger.
func
(
k
*
kq
)
Del
(
w
*
watched
)
{
syscall
.
Close
(
w
.
fd
)
delete
(
k
.
idLkp
,
w
.
fd
)
delete
(
k
.
pthLkp
,
w
.
p
)
}
func
inter2kq
(
n
interface
{})
syscall
.
Kevent_t
{
kq
,
ok
:=
n
.
(
syscall
.
Kevent_t
)
if
!
ok
{
panic
(
fmt
.
Sprintf
(
"kqueue: type should be Kevent_t, %T instead"
,
n
))
}
return
kq
}
// Init implements trigger.
func
(
k
*
kq
)
Init
()
(
err
error
)
{
if
k
.
fd
,
err
=
syscall
.
Kqueue
();
err
!=
nil
{
return
}
// Creates pipe used to stop `Kevent` call by registering it,
// watching read end and writing to other end of it.
if
err
=
syscall
.
Pipe
(
k
.
pipefds
[
:
]);
err
!=
nil
{
return
nonil
(
err
,
k
.
Close
())
}
var
kevn
[
1
]
syscall
.
Kevent_t
syscall
.
SetKevent
(
&
kevn
[
0
],
k
.
pipefds
[
0
],
syscall
.
EVFILT_READ
,
syscall
.
EV_ADD
)
if
_
,
err
=
syscall
.
Kevent
(
k
.
fd
,
kevn
[
:
],
nil
,
nil
);
err
!=
nil
{
return
nonil
(
err
,
k
.
Close
())
}
return
}
// Unwatch implements trigger.
func
(
k
*
kq
)
Unwatch
(
w
*
watched
)
(
err
error
)
{
var
kevn
[
1
]
syscall
.
Kevent_t
syscall
.
SetKevent
(
&
kevn
[
0
],
w
.
fd
,
syscall
.
EVFILT_VNODE
,
syscall
.
EV_DELETE
)
_
,
err
=
syscall
.
Kevent
(
k
.
fd
,
kevn
[
:
],
nil
,
nil
)
return
}
// Watch implements trigger.
func
(
k
*
kq
)
Watch
(
fi
os
.
FileInfo
,
w
*
watched
,
e
int64
)
(
err
error
)
{
var
kevn
[
1
]
syscall
.
Kevent_t
syscall
.
SetKevent
(
&
kevn
[
0
],
w
.
fd
,
syscall
.
EVFILT_VNODE
,
syscall
.
EV_ADD
|
syscall
.
EV_CLEAR
)
kevn
[
0
]
.
Fflags
=
uint32
(
e
)
_
,
err
=
syscall
.
Kevent
(
k
.
fd
,
kevn
[
:
],
nil
,
nil
)
return
}
// Wait implements trigger.
func
(
k
*
kq
)
Wait
()
(
interface
{},
error
)
{
var
(
kevn
[
1
]
syscall
.
Kevent_t
err
error
)
kevn
[
0
]
=
syscall
.
Kevent_t
{}
_
,
err
=
syscall
.
Kevent
(
k
.
fd
,
nil
,
kevn
[
:
],
nil
)
return
kevn
[
0
],
err
}
// Watched implements trigger.
func
(
k
*
kq
)
Watched
(
n
interface
{})
(
*
watched
,
int64
,
error
)
{
kevn
,
ok
:=
n
.
(
syscall
.
Kevent_t
)
if
!
ok
{
panic
(
fmt
.
Sprintf
(
"kq: type should be syscall.Kevent_t, %T instead"
,
kevn
))
}
if
_
,
ok
=
k
.
idLkp
[
int
(
kevn
.
Ident
)];
!
ok
{
return
nil
,
0
,
errNotWatched
}
return
k
.
idLkp
[
int
(
kevn
.
Ident
)],
int64
(
kevn
.
Fflags
),
nil
}
// IsStop implements trigger.
func
(
k
*
kq
)
IsStop
(
n
interface
{},
err
error
)
bool
{
return
int
(
inter2kq
(
n
)
.
Ident
)
==
k
.
pipefds
[
0
]
}
func
init
()
{
encode
=
func
(
e
Event
)
(
o
int64
)
{
// Create event is not supported by kqueue. Instead NoteWrite event will
// be registered. If this event will be reported on dir which is to be
// monitored for Create, dir will be rescanned and Create events will
// be generated and returned for new files. In case of files,
// if not requested NoteRename event is reported, it will be ignored.
o
=
int64
(
e
&^
Create
)
if
e
&
Write
!=
0
{
o
=
(
o
&^
int64
(
Write
))
|
int64
(
NoteWrite
)
}
if
e
&
Rename
!=
0
{
o
=
(
o
&^
int64
(
Rename
))
|
int64
(
NoteRename
)
}
if
e
&
Remove
!=
0
{
o
=
(
o
&^
int64
(
Remove
))
|
int64
(
NoteDelete
)
}
return
}
nat2not
=
map
[
Event
]
Event
{
NoteWrite
:
Write
,
NoteRename
:
Rename
,
NoteDelete
:
Remove
,
NoteExtend
:
Event
(
0
),
NoteAttrib
:
Event
(
0
),
NoteRevoke
:
Event
(
0
),
NoteLink
:
Event
(
0
),
}
not2nat
=
map
[
Event
]
Event
{
Write
:
NoteWrite
,
Rename
:
NoteRename
,
Remove
:
NoteDelete
,
}
}
Godeps/_workspace/src/github.com/rjeczalik/notify/watcher_readdcw.go
0 → 100644
View file @
ef63e9af
This diff is collapsed.
Click to expand it.
Godeps/_workspace/src/github.com/rjeczalik/notify/watcher_stub.go
0 → 100644
View file @
ef63e9af
// Copyright (c) 2014-2015 The Notify Authors. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
// +build !darwin,!linux,!freebsd,!dragonfly,!netbsd,!openbsd,!windows
// +build !kqueue,!solaris
package
notify
import
"errors"
type
stub
struct
{
error
}
// newWatcher stub.
func
newWatcher
(
chan
<-
EventInfo
)
watcher
{
return
stub
{
errors
.
New
(
"notify: not implemented"
)}
}
// Following methods implement notify.watcher interface.
func
(
s
stub
)
Watch
(
string
,
Event
)
error
{
return
s
}
func
(
s
stub
)
Rewatch
(
string
,
Event
,
Event
)
error
{
return
s
}
func
(
s
stub
)
Unwatch
(
string
)
(
err
error
)
{
return
s
}
func
(
s
stub
)
Close
()
error
{
return
s
}
Godeps/_workspace/src/github.com/rjeczalik/notify/watcher_trigger.go
0 → 100644
View file @
ef63e9af
This diff is collapsed.
Click to expand it.
Godeps/_workspace/src/github.com/rjeczalik/notify/watchpoint.go
0 → 100644
View file @
ef63e9af
// Copyright (c) 2014-2015 The Notify Authors. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
package
notify
// EventDiff describes a change to an event set - EventDiff[0] is an old state,
// while EventDiff[1] is a new state. If event set has not changed (old == new),
// functions typically return the None value.
type
eventDiff
[
2
]
Event
func
(
diff
eventDiff
)
Event
()
Event
{
return
diff
[
1
]
&^
diff
[
0
]
}
// Watchpoint
//
// The nil key holds total event set - logical sum for all registered events.
// It speeds up computing EventDiff for Add method.
//
// The rec key holds an event set for a watchpoints created by RecursiveWatch
// for a Watcher implementation which is not natively recursive.
type
watchpoint
map
[
chan
<-
EventInfo
]
Event
// None is an empty event diff, think null object.
var
none
eventDiff
// rec is just a placeholder
var
rec
=
func
()
(
ch
chan
<-
EventInfo
)
{
ch
=
make
(
chan
<-
EventInfo
)
close
(
ch
)
return
}()
func
(
wp
watchpoint
)
dryAdd
(
ch
chan
<-
EventInfo
,
e
Event
)
eventDiff
{
if
e
&^=
internal
;
wp
[
ch
]
&
e
==
e
{
return
none
}
total
:=
wp
[
ch
]
&^
internal
return
eventDiff
{
total
,
total
|
e
}
}
// Add assumes neither c nor e are nil or zero values.
func
(
wp
watchpoint
)
Add
(
c
chan
<-
EventInfo
,
e
Event
)
(
diff
eventDiff
)
{
wp
[
c
]
|=
e
diff
[
0
]
=
wp
[
nil
]
diff
[
1
]
=
diff
[
0
]
|
e
wp
[
nil
]
=
diff
[
1
]
&^
omit
// Strip diff from internal events.
diff
[
0
]
&^=
internal
diff
[
1
]
&^=
internal
if
diff
[
0
]
==
diff
[
1
]
{
return
none
}
return
}
func
(
wp
watchpoint
)
Del
(
c
chan
<-
EventInfo
,
e
Event
)
(
diff
eventDiff
)
{
wp
[
c
]
&^=
e
if
wp
[
c
]
==
0
{
delete
(
wp
,
c
)
}
diff
[
0
]
=
wp
[
nil
]
delete
(
wp
,
nil
)
if
len
(
wp
)
!=
0
{
// Recalculate total event set.
for
_
,
e
:=
range
wp
{
diff
[
1
]
|=
e
}
wp
[
nil
]
=
diff
[
1
]
&^
omit
}
// Strip diff from internal events.
diff
[
0
]
&^=
internal
diff
[
1
]
&^=
internal
if
diff
[
0
]
==
diff
[
1
]
{
return
none
}
return
}
func
(
wp
watchpoint
)
Dispatch
(
ei
EventInfo
,
extra
Event
)
{
e
:=
eventmask
(
ei
,
extra
)
if
!
matches
(
wp
[
nil
],
e
)
{
return
}
for
ch
,
eset
:=
range
wp
{
if
ch
!=
nil
&&
matches
(
eset
,
e
)
{
select
{
case
ch
<-
ei
:
default
:
// Drop event if receiver is too slow
dbgprintf
(
"dropped %s on %q: receiver too slow"
,
ei
.
Event
(),
ei
.
Path
())
}
}
}
}
func
(
wp
watchpoint
)
Total
()
Event
{
return
wp
[
nil
]
&^
internal
}
func
(
wp
watchpoint
)
IsRecursive
()
bool
{
return
wp
[
nil
]
&
recursive
!=
0
}
Godeps/_workspace/src/github.com/rjeczalik/notify/watchpoint_other.go
0 → 100644
View file @
ef63e9af
// Copyright (c) 2014-2015 The Notify Authors. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
// +build !windows
package
notify
// eventmask uses ei to create a new event which contains internal flags used by
// notify package logic.
func
eventmask
(
ei
EventInfo
,
extra
Event
)
Event
{
return
ei
.
Event
()
|
extra
}
// matches reports a match only when:
//
// - for user events, when event is present in the given set
// - for internal events, when additionaly both event and set have omit bit set
//
// Internal events must not be sent to user channels and vice versa.
func
matches
(
set
,
event
Event
)
bool
{
return
(
set
&
omit
)
^
(
event
&
omit
)
==
0
&&
set
&
event
==
event
}
Godeps/_workspace/src/github.com/rjeczalik/notify/watchpoint_readdcw.go
0 → 100644
View file @
ef63e9af
// Copyright (c) 2014-2015 The Notify Authors. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
// +build windows
package
notify
// eventmask uses ei to create a new event which contains internal flags used by
// notify package logic. If one of FileAction* masks is detected, this function
// adds corresponding FileNotifyChange* values. This allows non registered
// FileAction* events to be passed on.
func
eventmask
(
ei
EventInfo
,
extra
Event
)
(
e
Event
)
{
if
e
=
ei
.
Event
()
|
extra
;
e
&
fileActionAll
!=
0
{
if
ev
,
ok
:=
ei
.
(
*
event
);
ok
{
switch
ev
.
ftype
{
case
fTypeFile
:
e
|=
FileNotifyChangeFileName
case
fTypeDirectory
:
e
|=
FileNotifyChangeDirName
case
fTypeUnknown
:
e
|=
fileNotifyChangeModified
}
return
e
&^
fileActionAll
}
}
return
}
// matches reports a match only when:
//
// - for user events, when event is present in the given set
// - for internal events, when additionally both event and set have omit bit set
//
// Internal events must not be sent to user channels and vice versa.
func
matches
(
set
,
event
Event
)
bool
{
return
(
set
&
omit
)
^
(
event
&
omit
)
==
0
&&
(
set
&
event
==
event
||
set
&
fileNotifyChangeModified
&
event
!=
0
)
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment