mirror of
				https://github.com/jesseduffield/lazygit.git
				synced 2025-10-30 23:57:43 +02:00 
			
		
		
		
	Remove file watcher code
Now that we refresh upon focus, we can scrap this file watching code. Stefan says few git UIs use file watching, and I understand why: the reason this code was problematic in the first place is that watching files is expensive and if you have too many open file handles that can cause problems. Importantly: this code that's being removed was _already_ dead.
This commit is contained in:
		
							
								
								
									
										1
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								go.mod
									
									
									
									
									
								
							| @@ -9,7 +9,6 @@ require ( | ||||
| 	github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 | ||||
| 	github.com/creack/pty v1.1.11 | ||||
| 	github.com/fsmiamoto/git-todo-parser v0.0.5 | ||||
| 	github.com/fsnotify/fsnotify v1.4.7 | ||||
| 	github.com/gdamore/tcell/v2 v2.6.0 | ||||
| 	github.com/go-errors/errors v1.4.2 | ||||
| 	github.com/gookit/color v1.4.2 | ||||
|   | ||||
							
								
								
									
										1
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								go.sum
									
									
									
									
									
								
							| @@ -82,7 +82,6 @@ github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL | ||||
| github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= | ||||
| github.com/fsmiamoto/git-todo-parser v0.0.5 h1:Bhzd/vz/6Qm3udfkd6NO9fWfD3TpwR9ucp3N75/J5I8= | ||||
| github.com/fsmiamoto/git-todo-parser v0.0.5/go.mod h1:B+AgTbNE2BARvJqzXygThzqxLIaEWvwr2sxKYYb0Fas= | ||||
| github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= | ||||
| github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= | ||||
| github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= | ||||
| github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= | ||||
|   | ||||
| @@ -56,7 +56,6 @@ func (gui *Gui) resetHelpersAndControllers() { | ||||
| 		stagingHelper, | ||||
| 		mergeConflictsHelper, | ||||
| 		worktreeHelper, | ||||
| 		gui.fileWatcher, | ||||
| 	) | ||||
| 	diffHelper := helpers.NewDiffHelper(helperCommon) | ||||
| 	cherryPickHelper := helpers.NewCherryPickHelper( | ||||
|   | ||||
| @@ -28,7 +28,6 @@ type RefreshHelper struct { | ||||
| 	stagingHelper        *StagingHelper | ||||
| 	mergeConflictsHelper *MergeConflictsHelper | ||||
| 	worktreeHelper       *WorktreeHelper | ||||
| 	fileWatcher          types.IFileWatcher | ||||
| } | ||||
|  | ||||
| func NewRefreshHelper( | ||||
| @@ -39,7 +38,6 @@ func NewRefreshHelper( | ||||
| 	stagingHelper *StagingHelper, | ||||
| 	mergeConflictsHelper *MergeConflictsHelper, | ||||
| 	worktreeHelper *WorktreeHelper, | ||||
| 	fileWatcher types.IFileWatcher, | ||||
| ) *RefreshHelper { | ||||
| 	return &RefreshHelper{ | ||||
| 		c:                    c, | ||||
| @@ -49,7 +47,6 @@ func NewRefreshHelper( | ||||
| 		stagingHelper:        stagingHelper, | ||||
| 		mergeConflictsHelper: mergeConflictsHelper, | ||||
| 		worktreeHelper:       worktreeHelper, | ||||
| 		fileWatcher:          fileWatcher, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -548,10 +545,6 @@ func (self *RefreshHelper) refreshStateFiles() error { | ||||
| 	fileTreeViewModel.SetTree() | ||||
| 	fileTreeViewModel.RWMutex.Unlock() | ||||
|  | ||||
| 	if err := self.fileWatcher.AddFilesToFileWatcher(files); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,137 +0,0 @@ | ||||
| package gui | ||||
|  | ||||
| import ( | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
|  | ||||
| 	"github.com/fsnotify/fsnotify" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/models" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/types" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/utils" | ||||
| 	"github.com/sirupsen/logrus" | ||||
| ) | ||||
|  | ||||
| // macs for some bizarre reason cap the number of watchable files to 256. | ||||
| // there's no obvious platform agnostic way to check the situation of the user's | ||||
| // computer so we're just arbitrarily capping at 200. This isn't so bad because | ||||
| // file watching is only really an added bonus for faster refreshing. | ||||
| const MAX_WATCHED_FILES = 50 | ||||
|  | ||||
| var _ types.IFileWatcher = new(fileWatcher) | ||||
|  | ||||
| type fileWatcher struct { | ||||
| 	Watcher          *fsnotify.Watcher | ||||
| 	WatchedFilenames []string | ||||
| 	Log              *logrus.Entry | ||||
| 	Disabled         bool | ||||
| } | ||||
|  | ||||
| func NewFileWatcher(log *logrus.Entry) *fileWatcher { | ||||
| 	// TODO: get this going again, and ensure we don't see any crashes from it | ||||
| 	return &fileWatcher{ | ||||
| 		Disabled: true, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (w *fileWatcher) watchingFilename(filename string) bool { | ||||
| 	for _, watchedFilename := range w.WatchedFilenames { | ||||
| 		if watchedFilename == filename { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| func (w *fileWatcher) popOldestFilename() { | ||||
| 	// shift the last off the array to make way for this one | ||||
| 	oldestFilename := w.WatchedFilenames[0] | ||||
| 	w.WatchedFilenames = w.WatchedFilenames[1:] | ||||
| 	if err := w.Watcher.Remove(oldestFilename); err != nil { | ||||
| 		// swallowing errors here because it doesn't really matter if we can't unwatch a file | ||||
| 		w.Log.Error(err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (w *fileWatcher) watchFilename(filename string) { | ||||
| 	if err := w.Watcher.Add(filename); err != nil { | ||||
| 		// swallowing errors here because it doesn't really matter if we can't watch a file | ||||
| 		w.Log.Error(err) | ||||
| 	} | ||||
|  | ||||
| 	// assume we're watching it now to be safe | ||||
| 	w.WatchedFilenames = append(w.WatchedFilenames, filename) | ||||
| } | ||||
|  | ||||
| func (w *fileWatcher) AddFilesToFileWatcher(files []*models.File) error { | ||||
| 	if w.Disabled { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	if len(files) == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	// watch the files for changes | ||||
| 	dirName, err := os.Getwd() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	for _, file := range files[0:min(MAX_WATCHED_FILES, len(files))] { | ||||
| 		if file.Deleted { | ||||
| 			continue | ||||
| 		} | ||||
| 		filename := filepath.Join(dirName, file.Name) | ||||
| 		if w.watchingFilename(filename) { | ||||
| 			continue | ||||
| 		} | ||||
| 		if len(w.WatchedFilenames) > MAX_WATCHED_FILES { | ||||
| 			w.popOldestFilename() | ||||
| 		} | ||||
|  | ||||
| 		w.watchFilename(filename) | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func min(a int, b int) int { | ||||
| 	if a < b { | ||||
| 		return a | ||||
| 	} | ||||
| 	return b | ||||
| } | ||||
|  | ||||
| // NOTE: given that we often edit files ourselves, this may make us end up refreshing files too often | ||||
| // TODO: consider watching the whole directory recursively (could be more expensive) | ||||
| func (gui *Gui) WatchFilesForChanges() { | ||||
| 	gui.fileWatcher = NewFileWatcher(gui.Log) | ||||
| 	if gui.fileWatcher.Disabled { | ||||
| 		return | ||||
| 	} | ||||
| 	go utils.Safe(func() { | ||||
| 		for { | ||||
| 			select { | ||||
| 			// watch for events | ||||
| 			case event := <-gui.fileWatcher.Watcher.Events: | ||||
| 				if event.Op == fsnotify.Chmod { | ||||
| 					// for some reason we pick up chmod events when they don't actually happen | ||||
| 					continue | ||||
| 				} | ||||
| 				// only refresh if we're not already | ||||
| 				if !gui.IsRefreshingFiles { | ||||
| 					gui.c.OnUIThread(func() error { | ||||
| 						// TODO: find out if refresh needs to be run on the UI thread | ||||
| 						return gui.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES}}) | ||||
| 					}) | ||||
| 				} | ||||
|  | ||||
| 			// watch for errors | ||||
| 			case err := <-gui.fileWatcher.Watcher.Errors: | ||||
| 				if err != nil { | ||||
| 					gui.c.Log.Error(err) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	}) | ||||
| } | ||||
| @@ -72,7 +72,6 @@ type Gui struct { | ||||
| 	Updater              *updates.Updater | ||||
| 	statusManager        *status.StatusManager | ||||
| 	waitForIntro         sync.WaitGroup | ||||
| 	fileWatcher          *fileWatcher | ||||
| 	viewBufferManagerMap map[string]*tasks.ViewBufferManager | ||||
| 	// holds a mapping of view names to ptmx's. This is for rendering command outputs | ||||
| 	// from within a pty. The point of keeping track of them is so that if we re-size | ||||
| @@ -476,8 +475,6 @@ func NewGui( | ||||
| 		afterLayoutFuncs: make(chan func() error, 1000), | ||||
| 	} | ||||
|  | ||||
| 	gui.WatchFilesForChanges() | ||||
|  | ||||
| 	gui.PopupHandler = popup.NewPopupHandler( | ||||
| 		cmn, | ||||
| 		func(ctx goContext.Context, opts types.CreatePopupPanelOpts) error { | ||||
| @@ -680,10 +677,6 @@ func (gui *Gui) RunAndHandleError(startArgs appTypes.StartArgs) error { | ||||
| 				manager.Close() | ||||
| 			} | ||||
|  | ||||
| 			if !gui.fileWatcher.Disabled { | ||||
| 				gui.fileWatcher.Watcher.Close() | ||||
| 			} | ||||
|  | ||||
| 			close(gui.stopChan) | ||||
|  | ||||
| 			switch err { | ||||
|   | ||||
| @@ -282,10 +282,6 @@ const ( | ||||
| 	COMPLETE | ||||
| ) | ||||
|  | ||||
| type IFileWatcher interface { | ||||
| 	AddFilesToFileWatcher(files []*models.File) error | ||||
| } | ||||
|  | ||||
| // screen sizing determines how much space your selected window takes up (window | ||||
| // as in panel, not your terminal's window). Sometimes you want a bit more space | ||||
| // to see the contents of a panel, and this keeps track of how much maximisation | ||||
|   | ||||
							
								
								
									
										6
									
								
								vendor/github.com/fsnotify/fsnotify/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								vendor/github.com/fsnotify/fsnotify/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,6 +0,0 @@ | ||||
| # Setup a Global .gitignore for OS and editor generated files: | ||||
| # https://help.github.com/articles/ignoring-files | ||||
| # git config --global core.excludesfile ~/.gitignore_global | ||||
|  | ||||
| .vagrant | ||||
| *.sublime-project | ||||
							
								
								
									
										52
									
								
								vendor/github.com/fsnotify/fsnotify/AUTHORS
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										52
									
								
								vendor/github.com/fsnotify/fsnotify/AUTHORS
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,52 +0,0 @@ | ||||
| # Names should be added to this file as | ||||
| #	Name or Organization <email address> | ||||
| # The email address is not required for organizations. | ||||
|  | ||||
| # You can update this list using the following command: | ||||
| # | ||||
| #   $ git shortlog -se | awk '{print $2 " " $3 " " $4}' | ||||
|  | ||||
| # Please keep the list sorted. | ||||
|  | ||||
| Aaron L <aaron@bettercoder.net> | ||||
| Adrien Bustany <adrien@bustany.org> | ||||
| Amit Krishnan <amit.krishnan@oracle.com> | ||||
| Anmol Sethi <me@anmol.io> | ||||
| Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | ||||
| Bruno Bigras <bigras.bruno@gmail.com> | ||||
| Caleb Spare <cespare@gmail.com> | ||||
| Case Nelson <case@teammating.com> | ||||
| Chris Howey <chris@howey.me> <howeyc@gmail.com> | ||||
| Christoffer Buchholz <christoffer.buchholz@gmail.com> | ||||
| Daniel Wagner-Hall <dawagner@gmail.com> | ||||
| Dave Cheney <dave@cheney.net> | ||||
| Evan Phoenix <evan@fallingsnow.net> | ||||
| Francisco Souza <f@souza.cc> | ||||
| Hari haran <hariharan.uno@gmail.com> | ||||
| John C Barstow | ||||
| Kelvin Fo <vmirage@gmail.com> | ||||
| Ken-ichirou MATSUZAWA <chamas@h4.dion.ne.jp> | ||||
| Matt Layher <mdlayher@gmail.com> | ||||
| Nathan Youngman <git@nathany.com> | ||||
| Nickolai Zeldovich <nickolai@csail.mit.edu> | ||||
| Patrick <patrick@dropbox.com> | ||||
| Paul Hammond <paul@paulhammond.org> | ||||
| Pawel Knap <pawelknap88@gmail.com> | ||||
| Pieter Droogendijk <pieter@binky.org.uk> | ||||
| Pursuit92 <JoshChase@techpursuit.net> | ||||
| Riku Voipio <riku.voipio@linaro.org> | ||||
| Rob Figueiredo <robfig@gmail.com> | ||||
| Rodrigo Chiossi <rodrigochiossi@gmail.com> | ||||
| Slawek Ligus <root@ooz.ie> | ||||
| Soge Zhang <zhssoge@gmail.com> | ||||
| Tiffany Jernigan <tiffany.jernigan@intel.com> | ||||
| Tilak Sharma <tilaks@google.com> | ||||
| Tom Payne <twpayne@gmail.com> | ||||
| Travis Cline <travis.cline@gmail.com> | ||||
| Tudor Golubenco <tudor.g@gmail.com> | ||||
| Vahe Khachikyan <vahe@live.ca> | ||||
| Yukang <moorekang@gmail.com> | ||||
| bronze1man <bronze1man@gmail.com> | ||||
| debrando <denis.brandolini@gmail.com> | ||||
| henrikedwards <henrik.edwards@gmail.com> | ||||
| 铁哥 <guotie.9@gmail.com> | ||||
							
								
								
									
										317
									
								
								vendor/github.com/fsnotify/fsnotify/CHANGELOG.md
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										317
									
								
								vendor/github.com/fsnotify/fsnotify/CHANGELOG.md
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,317 +0,0 @@ | ||||
| # Changelog | ||||
|  | ||||
| ## v1.4.7 / 2018-01-09 | ||||
|  | ||||
| * BSD/macOS: Fix possible deadlock on closing the watcher on kqueue (thanks @nhooyr and @glycerine) | ||||
| * Tests: Fix missing verb on format string (thanks @rchiossi) | ||||
| * Linux: Fix deadlock in Remove (thanks @aarondl) | ||||
| * Linux: Watch.Add improvements (avoid race, fix consistency, reduce garbage) (thanks @twpayne) | ||||
| * Docs: Moved FAQ into the README (thanks @vahe) | ||||
| * Linux: Properly handle inotify's IN_Q_OVERFLOW event (thanks @zeldovich) | ||||
| * Docs: replace references to OS X with macOS | ||||
|  | ||||
| ## v1.4.2 / 2016-10-10 | ||||
|  | ||||
| * Linux: use InotifyInit1 with IN_CLOEXEC to stop leaking a file descriptor to a child process when using fork/exec [#178](https://github.com/fsnotify/fsnotify/pull/178) (thanks @pattyshack) | ||||
|  | ||||
| ## v1.4.1 / 2016-10-04 | ||||
|  | ||||
| * Fix flaky inotify stress test on Linux [#177](https://github.com/fsnotify/fsnotify/pull/177) (thanks @pattyshack) | ||||
|  | ||||
| ## v1.4.0 / 2016-10-01 | ||||
|  | ||||
| * add a String() method to Event.Op [#165](https://github.com/fsnotify/fsnotify/pull/165) (thanks @oozie) | ||||
|  | ||||
| ## v1.3.1 / 2016-06-28 | ||||
|  | ||||
| * Windows: fix for double backslash when watching the root of a drive [#151](https://github.com/fsnotify/fsnotify/issues/151) (thanks @brunoqc) | ||||
|  | ||||
| ## v1.3.0 / 2016-04-19 | ||||
|  | ||||
| * Support linux/arm64 by [patching](https://go-review.googlesource.com/#/c/21971/) x/sys/unix and switching to to it from syscall (thanks @suihkulokki) [#135](https://github.com/fsnotify/fsnotify/pull/135) | ||||
|  | ||||
| ## v1.2.10 / 2016-03-02 | ||||
|  | ||||
| * Fix golint errors in windows.go [#121](https://github.com/fsnotify/fsnotify/pull/121) (thanks @tiffanyfj) | ||||
|  | ||||
| ## v1.2.9 / 2016-01-13 | ||||
|  | ||||
| kqueue: Fix logic for CREATE after REMOVE [#111](https://github.com/fsnotify/fsnotify/pull/111) (thanks @bep) | ||||
|  | ||||
| ## v1.2.8 / 2015-12-17 | ||||
|  | ||||
| * kqueue: fix race condition in Close [#105](https://github.com/fsnotify/fsnotify/pull/105) (thanks @djui for reporting the issue and @ppknap for writing a failing test) | ||||
| * inotify: fix race in test | ||||
| * enable race detection for continuous integration (Linux, Mac, Windows) | ||||
|  | ||||
| ## v1.2.5 / 2015-10-17 | ||||
|  | ||||
| * inotify: use epoll_create1 for arm64 support (requires Linux 2.6.27 or later) [#100](https://github.com/fsnotify/fsnotify/pull/100) (thanks @suihkulokki) | ||||
| * inotify: fix path leaks [#73](https://github.com/fsnotify/fsnotify/pull/73) (thanks @chamaken) | ||||
| * kqueue: watch for rename events on subdirectories [#83](https://github.com/fsnotify/fsnotify/pull/83) (thanks @guotie) | ||||
| * kqueue: avoid infinite loops from symlinks cycles [#101](https://github.com/fsnotify/fsnotify/pull/101) (thanks @illicitonion) | ||||
|  | ||||
| ## v1.2.1 / 2015-10-14 | ||||
|  | ||||
| * kqueue: don't watch named pipes [#98](https://github.com/fsnotify/fsnotify/pull/98) (thanks @evanphx) | ||||
|  | ||||
| ## v1.2.0 / 2015-02-08 | ||||
|  | ||||
| * inotify: use epoll to wake up readEvents [#66](https://github.com/fsnotify/fsnotify/pull/66) (thanks @PieterD) | ||||
| * inotify: closing watcher should now always shut down goroutine [#63](https://github.com/fsnotify/fsnotify/pull/63) (thanks @PieterD) | ||||
| * kqueue: close kqueue after removing watches, fixes [#59](https://github.com/fsnotify/fsnotify/issues/59) | ||||
|  | ||||
| ## v1.1.1 / 2015-02-05 | ||||
|  | ||||
| * inotify: Retry read on EINTR [#61](https://github.com/fsnotify/fsnotify/issues/61) (thanks @PieterD) | ||||
|  | ||||
| ## v1.1.0 / 2014-12-12 | ||||
|  | ||||
| * kqueue: rework internals [#43](https://github.com/fsnotify/fsnotify/pull/43) | ||||
|     * add low-level functions | ||||
|     * only need to store flags on directories | ||||
|     * less mutexes [#13](https://github.com/fsnotify/fsnotify/issues/13) | ||||
|     * done can be an unbuffered channel | ||||
|     * remove calls to os.NewSyscallError | ||||
| * More efficient string concatenation for Event.String() [#52](https://github.com/fsnotify/fsnotify/pull/52) (thanks @mdlayher) | ||||
| * kqueue: fix regression in  rework causing subdirectories to be watched [#48](https://github.com/fsnotify/fsnotify/issues/48) | ||||
| * kqueue: cleanup internal watch before sending remove event [#51](https://github.com/fsnotify/fsnotify/issues/51) | ||||
|  | ||||
| ## v1.0.4 / 2014-09-07 | ||||
|  | ||||
| * kqueue: add dragonfly to the build tags. | ||||
| * Rename source code files, rearrange code so exported APIs are at the top. | ||||
| * Add done channel to example code. [#37](https://github.com/fsnotify/fsnotify/pull/37) (thanks @chenyukang) | ||||
|  | ||||
| ## v1.0.3 / 2014-08-19 | ||||
|  | ||||
| * [Fix] Windows MOVED_TO now translates to Create like on BSD and Linux. [#36](https://github.com/fsnotify/fsnotify/issues/36) | ||||
|  | ||||
| ## v1.0.2 / 2014-08-17 | ||||
|  | ||||
| * [Fix] Missing create events on macOS. [#14](https://github.com/fsnotify/fsnotify/issues/14) (thanks @zhsso) | ||||
| * [Fix] Make ./path and path equivalent. (thanks @zhsso) | ||||
|  | ||||
| ## v1.0.0 / 2014-08-15 | ||||
|  | ||||
| * [API] Remove AddWatch on Windows, use Add. | ||||
| * Improve documentation for exported identifiers. [#30](https://github.com/fsnotify/fsnotify/issues/30) | ||||
| * Minor updates based on feedback from golint. | ||||
|  | ||||
| ## dev / 2014-07-09 | ||||
|  | ||||
| * Moved to [github.com/fsnotify/fsnotify](https://github.com/fsnotify/fsnotify). | ||||
| * Use os.NewSyscallError instead of returning errno (thanks @hariharan-uno) | ||||
|  | ||||
| ## dev / 2014-07-04 | ||||
|  | ||||
| * kqueue: fix incorrect mutex used in Close() | ||||
| * Update example to demonstrate usage of Op. | ||||
|  | ||||
| ## dev / 2014-06-28 | ||||
|  | ||||
| * [API] Don't set the Write Op for attribute notifications [#4](https://github.com/fsnotify/fsnotify/issues/4) | ||||
| * Fix for String() method on Event (thanks Alex Brainman) | ||||
| * Don't build on Plan 9 or Solaris (thanks @4ad) | ||||
|  | ||||
| ## dev / 2014-06-21 | ||||
|  | ||||
| * Events channel of type Event rather than *Event. | ||||
| * [internal] use syscall constants directly for inotify and kqueue. | ||||
| * [internal] kqueue: rename events to kevents and fileEvent to event. | ||||
|  | ||||
| ## dev / 2014-06-19 | ||||
|  | ||||
| * Go 1.3+ required on Windows (uses syscall.ERROR_MORE_DATA internally). | ||||
| * [internal] remove cookie from Event struct (unused). | ||||
| * [internal] Event struct has the same definition across every OS. | ||||
| * [internal] remove internal watch and removeWatch methods. | ||||
|  | ||||
| ## dev / 2014-06-12 | ||||
|  | ||||
| * [API] Renamed Watch() to Add() and RemoveWatch() to Remove(). | ||||
| * [API] Pluralized channel names: Events and Errors. | ||||
| * [API] Renamed FileEvent struct to Event. | ||||
| * [API] Op constants replace methods like IsCreate(). | ||||
|  | ||||
| ## dev / 2014-06-12 | ||||
|  | ||||
| * Fix data race on kevent buffer (thanks @tilaks) [#98](https://github.com/howeyc/fsnotify/pull/98) | ||||
|  | ||||
| ## dev / 2014-05-23 | ||||
|  | ||||
| * [API] Remove current implementation of WatchFlags. | ||||
|     * current implementation doesn't take advantage of OS for efficiency | ||||
|     * provides little benefit over filtering events as they are received, but has  extra bookkeeping and mutexes | ||||
|     * no tests for the current implementation | ||||
|     * not fully implemented on Windows [#93](https://github.com/howeyc/fsnotify/issues/93#issuecomment-39285195) | ||||
|  | ||||
| ## v0.9.3 / 2014-12-31 | ||||
|  | ||||
| * kqueue: cleanup internal watch before sending remove event [#51](https://github.com/fsnotify/fsnotify/issues/51) | ||||
|  | ||||
| ## v0.9.2 / 2014-08-17 | ||||
|  | ||||
| * [Backport] Fix missing create events on macOS. [#14](https://github.com/fsnotify/fsnotify/issues/14) (thanks @zhsso) | ||||
|  | ||||
| ## v0.9.1 / 2014-06-12 | ||||
|  | ||||
| * Fix data race on kevent buffer (thanks @tilaks) [#98](https://github.com/howeyc/fsnotify/pull/98) | ||||
|  | ||||
| ## v0.9.0 / 2014-01-17 | ||||
|  | ||||
| * IsAttrib() for events that only concern a file's metadata [#79][] (thanks @abustany) | ||||
| * [Fix] kqueue: fix deadlock [#77][] (thanks @cespare) | ||||
| * [NOTICE] Development has moved to `code.google.com/p/go.exp/fsnotify` in preparation for inclusion in the Go standard library. | ||||
|  | ||||
| ## v0.8.12 / 2013-11-13 | ||||
|  | ||||
| * [API] Remove FD_SET and friends from Linux adapter | ||||
|  | ||||
| ## v0.8.11 / 2013-11-02 | ||||
|  | ||||
| * [Doc] Add Changelog [#72][] (thanks @nathany) | ||||
| * [Doc] Spotlight and double modify events on macOS [#62][] (reported by @paulhammond) | ||||
|  | ||||
| ## v0.8.10 / 2013-10-19 | ||||
|  | ||||
| * [Fix] kqueue: remove file watches when parent directory is removed [#71][] (reported by @mdwhatcott) | ||||
| * [Fix] kqueue: race between Close and readEvents [#70][] (reported by @bernerdschaefer) | ||||
| * [Doc] specify OS-specific limits in README (thanks @debrando) | ||||
|  | ||||
| ## v0.8.9 / 2013-09-08 | ||||
|  | ||||
| * [Doc] Contributing (thanks @nathany) | ||||
| * [Doc] update package path in example code [#63][] (thanks @paulhammond) | ||||
| * [Doc] GoCI badge in README (Linux only) [#60][] | ||||
| * [Doc] Cross-platform testing with Vagrant  [#59][] (thanks @nathany) | ||||
|  | ||||
| ## v0.8.8 / 2013-06-17 | ||||
|  | ||||
| * [Fix] Windows: handle `ERROR_MORE_DATA` on Windows [#49][] (thanks @jbowtie) | ||||
|  | ||||
| ## v0.8.7 / 2013-06-03 | ||||
|  | ||||
| * [API] Make syscall flags internal | ||||
| * [Fix] inotify: ignore event changes | ||||
| * [Fix] race in symlink test [#45][] (reported by @srid) | ||||
| * [Fix] tests on Windows | ||||
| * lower case error messages | ||||
|  | ||||
| ## v0.8.6 / 2013-05-23 | ||||
|  | ||||
| * kqueue: Use EVT_ONLY flag on Darwin | ||||
| * [Doc] Update README with full example | ||||
|  | ||||
| ## v0.8.5 / 2013-05-09 | ||||
|  | ||||
| * [Fix] inotify: allow monitoring of "broken" symlinks (thanks @tsg) | ||||
|  | ||||
| ## v0.8.4 / 2013-04-07 | ||||
|  | ||||
| * [Fix] kqueue: watch all file events [#40][] (thanks @ChrisBuchholz) | ||||
|  | ||||
| ## v0.8.3 / 2013-03-13 | ||||
|  | ||||
| * [Fix] inoitfy/kqueue memory leak [#36][] (reported by @nbkolchin) | ||||
| * [Fix] kqueue: use fsnFlags for watching a directory [#33][] (reported by @nbkolchin) | ||||
|  | ||||
| ## v0.8.2 / 2013-02-07 | ||||
|  | ||||
| * [Doc] add Authors | ||||
| * [Fix] fix data races for map access [#29][] (thanks @fsouza) | ||||
|  | ||||
| ## v0.8.1 / 2013-01-09 | ||||
|  | ||||
| * [Fix] Windows path separators | ||||
| * [Doc] BSD License | ||||
|  | ||||
| ## v0.8.0 / 2012-11-09 | ||||
|  | ||||
| * kqueue: directory watching improvements (thanks @vmirage) | ||||
| * inotify: add `IN_MOVED_TO` [#25][] (requested by @cpisto) | ||||
| * [Fix] kqueue: deleting watched directory [#24][] (reported by @jakerr) | ||||
|  | ||||
| ## v0.7.4 / 2012-10-09 | ||||
|  | ||||
| * [Fix] inotify: fixes from https://codereview.appspot.com/5418045/ (ugorji) | ||||
| * [Fix] kqueue: preserve watch flags when watching for delete [#21][] (reported by @robfig) | ||||
| * [Fix] kqueue: watch the directory even if it isn't a new watch (thanks @robfig) | ||||
| * [Fix] kqueue: modify after recreation of file | ||||
|  | ||||
| ## v0.7.3 / 2012-09-27 | ||||
|  | ||||
| * [Fix] kqueue: watch with an existing folder inside the watched folder (thanks @vmirage) | ||||
| * [Fix] kqueue: no longer get duplicate CREATE events | ||||
|  | ||||
| ## v0.7.2 / 2012-09-01 | ||||
|  | ||||
| * kqueue: events for created directories | ||||
|  | ||||
| ## v0.7.1 / 2012-07-14 | ||||
|  | ||||
| * [Fix] for renaming files | ||||
|  | ||||
| ## v0.7.0 / 2012-07-02 | ||||
|  | ||||
| * [Feature] FSNotify flags | ||||
| * [Fix] inotify: Added file name back to event path | ||||
|  | ||||
| ## v0.6.0 / 2012-06-06 | ||||
|  | ||||
| * kqueue: watch files after directory created (thanks @tmc) | ||||
|  | ||||
| ## v0.5.1 / 2012-05-22 | ||||
|  | ||||
| * [Fix] inotify: remove all watches before Close() | ||||
|  | ||||
| ## v0.5.0 / 2012-05-03 | ||||
|  | ||||
| * [API] kqueue: return errors during watch instead of sending over channel | ||||
| * kqueue: match symlink behavior on Linux | ||||
| * inotify: add `DELETE_SELF` (requested by @taralx) | ||||
| * [Fix] kqueue: handle EINTR (reported by @robfig) | ||||
| * [Doc] Godoc example [#1][] (thanks @davecheney) | ||||
|  | ||||
| ## v0.4.0 / 2012-03-30 | ||||
|  | ||||
| * Go 1 released: build with go tool | ||||
| * [Feature] Windows support using winfsnotify | ||||
| * Windows does not have attribute change notifications | ||||
| * Roll attribute notifications into IsModify | ||||
|  | ||||
| ## v0.3.0 / 2012-02-19 | ||||
|  | ||||
| * kqueue: add files when watch directory | ||||
|  | ||||
| ## v0.2.0 / 2011-12-30 | ||||
|  | ||||
| * update to latest Go weekly code | ||||
|  | ||||
| ## v0.1.0 / 2011-10-19 | ||||
|  | ||||
| * kqueue: add watch on file creation to match inotify | ||||
| * kqueue: create file event | ||||
| * inotify: ignore `IN_IGNORED` events | ||||
| * event String() | ||||
| * linux: common FileEvent functions | ||||
| * initial commit | ||||
|  | ||||
| [#79]: https://github.com/howeyc/fsnotify/pull/79 | ||||
| [#77]: https://github.com/howeyc/fsnotify/pull/77 | ||||
| [#72]: https://github.com/howeyc/fsnotify/issues/72 | ||||
| [#71]: https://github.com/howeyc/fsnotify/issues/71 | ||||
| [#70]: https://github.com/howeyc/fsnotify/issues/70 | ||||
| [#63]: https://github.com/howeyc/fsnotify/issues/63 | ||||
| [#62]: https://github.com/howeyc/fsnotify/issues/62 | ||||
| [#60]: https://github.com/howeyc/fsnotify/issues/60 | ||||
| [#59]: https://github.com/howeyc/fsnotify/issues/59 | ||||
| [#49]: https://github.com/howeyc/fsnotify/issues/49 | ||||
| [#45]: https://github.com/howeyc/fsnotify/issues/45 | ||||
| [#40]: https://github.com/howeyc/fsnotify/issues/40 | ||||
| [#36]: https://github.com/howeyc/fsnotify/issues/36 | ||||
| [#33]: https://github.com/howeyc/fsnotify/issues/33 | ||||
| [#29]: https://github.com/howeyc/fsnotify/issues/29 | ||||
| [#25]: https://github.com/howeyc/fsnotify/issues/25 | ||||
| [#24]: https://github.com/howeyc/fsnotify/issues/24 | ||||
| [#21]: https://github.com/howeyc/fsnotify/issues/21 | ||||
							
								
								
									
										77
									
								
								vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										77
									
								
								vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,77 +0,0 @@ | ||||
| # Contributing | ||||
|  | ||||
| ## Issues | ||||
|  | ||||
| * Request features and report bugs using the [GitHub Issue Tracker](https://github.com/fsnotify/fsnotify/issues). | ||||
| * Please indicate the platform you are using fsnotify on. | ||||
| * A code example to reproduce the problem is appreciated. | ||||
|  | ||||
| ## Pull Requests | ||||
|  | ||||
| ### Contributor License Agreement | ||||
|  | ||||
| fsnotify is derived from code in the [golang.org/x/exp](https://godoc.org/golang.org/x/exp) package and it may be included [in the standard library](https://github.com/fsnotify/fsnotify/issues/1) in the future. Therefore fsnotify carries the same [LICENSE](https://github.com/fsnotify/fsnotify/blob/master/LICENSE) as Go. Contributors retain their copyright, so you need to fill out a short form before we can accept your contribution: [Google Individual Contributor License Agreement](https://developers.google.com/open-source/cla/individual). | ||||
|  | ||||
| Please indicate that you have signed the CLA in your pull request. | ||||
|  | ||||
| ### How fsnotify is Developed | ||||
|  | ||||
| * Development is done on feature branches. | ||||
| * Tests are run on BSD, Linux, macOS and Windows. | ||||
| * Pull requests are reviewed and [applied to master][am] using [hub][]. | ||||
|   * Maintainers may modify or squash commits rather than asking contributors to. | ||||
| * To issue a new release, the maintainers will: | ||||
|   * Update the CHANGELOG | ||||
|   * Tag a version, which will become available through gopkg.in. | ||||
|   | ||||
| ### How to Fork | ||||
|  | ||||
| For smooth sailing, always use the original import path. Installing with `go get` makes this easy.  | ||||
|  | ||||
| 1. Install from GitHub (`go get -u github.com/fsnotify/fsnotify`) | ||||
| 2. Create your feature branch (`git checkout -b my-new-feature`) | ||||
| 3. Ensure everything works and the tests pass (see below) | ||||
| 4. Commit your changes (`git commit -am 'Add some feature'`) | ||||
|  | ||||
| Contribute upstream: | ||||
|  | ||||
| 1. Fork fsnotify on GitHub | ||||
| 2. Add your remote (`git remote add fork git@github.com:mycompany/repo.git`) | ||||
| 3. Push to the branch (`git push fork my-new-feature`) | ||||
| 4. Create a new Pull Request on GitHub | ||||
|  | ||||
| This workflow is [thoroughly explained by Katrina Owen](https://splice.com/blog/contributing-open-source-git-repositories-go/). | ||||
|  | ||||
| ### Testing | ||||
|  | ||||
| fsnotify uses build tags to compile different code on Linux, BSD, macOS, and Windows. | ||||
|  | ||||
| Before doing a pull request, please do your best to test your changes on multiple platforms, and list which platforms you were able/unable to test on. | ||||
|  | ||||
| To aid in cross-platform testing there is a Vagrantfile for Linux and BSD. | ||||
|  | ||||
| * Install [Vagrant](http://www.vagrantup.com/) and [VirtualBox](https://www.virtualbox.org/) | ||||
| * Setup [Vagrant Gopher](https://github.com/nathany/vagrant-gopher) in your `src` folder. | ||||
| * Run `vagrant up` from the project folder. You can also setup just one box with `vagrant up linux` or `vagrant up bsd` (note: the BSD box doesn't support Windows hosts at this time, and NFS may prompt for your host OS password) | ||||
| * Once setup, you can run the test suite on a given OS with a single command `vagrant ssh linux -c 'cd fsnotify/fsnotify; go test'`. | ||||
| * When you're done, you will want to halt or destroy the Vagrant boxes. | ||||
|  | ||||
| Notice: fsnotify file system events won't trigger in shared folders. The tests get around this limitation by using the /tmp directory. | ||||
|  | ||||
| Right now there is no equivalent solution for Windows and macOS, but there are Windows VMs [freely available from Microsoft](http://www.modern.ie/en-us/virtualization-tools#downloads). | ||||
|  | ||||
| ### Maintainers | ||||
|  | ||||
| Help maintaining fsnotify is welcome. To be a maintainer: | ||||
|  | ||||
| * Submit a pull request and sign the CLA as above. | ||||
| * You must be able to run the test suite on Mac, Windows, Linux and BSD. | ||||
|  | ||||
| To keep master clean, the fsnotify project uses the "apply mail" workflow outlined in Nathaniel Talbott's post ["Merge pull request" Considered Harmful][am]. This requires installing [hub][]. | ||||
|  | ||||
| All code changes should be internal pull requests. | ||||
|  | ||||
| Releases are tagged using [Semantic Versioning](http://semver.org/). | ||||
|  | ||||
| [hub]: https://github.com/github/hub | ||||
| [am]: http://blog.spreedly.com/2014/06/24/merge-pull-request-considered-harmful/#.VGa5yZPF_Zs | ||||
							
								
								
									
										28
									
								
								vendor/github.com/fsnotify/fsnotify/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										28
									
								
								vendor/github.com/fsnotify/fsnotify/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,28 +0,0 @@ | ||||
| Copyright (c) 2012 The Go Authors. All rights reserved. | ||||
| Copyright (c) 2012 fsnotify Authors. All rights reserved. | ||||
|  | ||||
| Redistribution and use in source and binary forms, with or without | ||||
| modification, are permitted provided that the following conditions are | ||||
| met: | ||||
|  | ||||
|    * Redistributions of source code must retain the above copyright | ||||
| notice, this list of conditions and the following disclaimer. | ||||
|    * Redistributions in binary form must reproduce the above | ||||
| copyright notice, this list of conditions and the following disclaimer | ||||
| in the documentation and/or other materials provided with the | ||||
| distribution. | ||||
|    * Neither the name of Google Inc. nor the names of its | ||||
| contributors may be used to endorse or promote products derived from | ||||
| this software without specific prior written permission. | ||||
|  | ||||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
							
								
								
									
										79
									
								
								vendor/github.com/fsnotify/fsnotify/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										79
									
								
								vendor/github.com/fsnotify/fsnotify/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,79 +0,0 @@ | ||||
| # File system notifications for Go | ||||
|  | ||||
| [](https://godoc.org/github.com/fsnotify/fsnotify) [](https://goreportcard.com/report/github.com/fsnotify/fsnotify) | ||||
|  | ||||
| fsnotify utilizes [golang.org/x/sys](https://godoc.org/golang.org/x/sys) rather than `syscall` from the standard library. Ensure you have the latest version installed by running: | ||||
|  | ||||
| ```console | ||||
| go get -u golang.org/x/sys/... | ||||
| ``` | ||||
|  | ||||
| Cross platform: Windows, Linux, BSD and macOS. | ||||
|  | ||||
| |Adapter   |OS        |Status    | | ||||
| |----------|----------|----------| | ||||
| |inotify   |Linux 2.6.27 or later, Android\*|Supported [](https://travis-ci.org/fsnotify/fsnotify)| | ||||
| |kqueue    |BSD, macOS, iOS\*|Supported [](https://travis-ci.org/fsnotify/fsnotify)| | ||||
| |ReadDirectoryChangesW|Windows|Supported [](https://ci.appveyor.com/project/NathanYoungman/fsnotify/branch/master)| | ||||
| |FSEvents  |macOS         |[Planned](https://github.com/fsnotify/fsnotify/issues/11)| | ||||
| |FEN       |Solaris 11    |[In Progress](https://github.com/fsnotify/fsnotify/issues/12)| | ||||
| |fanotify  |Linux 2.6.37+ | | | ||||
| |USN Journals |Windows    |[Maybe](https://github.com/fsnotify/fsnotify/issues/53)| | ||||
| |Polling   |*All*         |[Maybe](https://github.com/fsnotify/fsnotify/issues/9)| | ||||
|  | ||||
| \* Android and iOS are untested. | ||||
|  | ||||
| Please see [the documentation](https://godoc.org/github.com/fsnotify/fsnotify) and consult the [FAQ](#faq) for usage information. | ||||
|  | ||||
| ## API stability | ||||
|  | ||||
| fsnotify is a fork of [howeyc/fsnotify](https://godoc.org/github.com/howeyc/fsnotify) with a new API as of v1.0. The API is based on [this design document](http://goo.gl/MrYxyA).  | ||||
|  | ||||
| All [releases](https://github.com/fsnotify/fsnotify/releases) are tagged based on [Semantic Versioning](http://semver.org/). Further API changes are [planned](https://github.com/fsnotify/fsnotify/milestones), and will be tagged with a new major revision number. | ||||
|  | ||||
| Go 1.6 supports dependencies located in the `vendor/` folder. Unless you are creating a library, it is recommended that you copy fsnotify into `vendor/github.com/fsnotify/fsnotify` within your project, and likewise for `golang.org/x/sys`. | ||||
|  | ||||
| ## Contributing | ||||
|  | ||||
| Please refer to [CONTRIBUTING][] before opening an issue or pull request. | ||||
|  | ||||
| ## Example | ||||
|  | ||||
| See [example_test.go](https://github.com/fsnotify/fsnotify/blob/master/example_test.go). | ||||
|  | ||||
| ## FAQ | ||||
|  | ||||
| **When a file is moved to another directory is it still being watched?** | ||||
|  | ||||
| No (it shouldn't be, unless you are watching where it was moved to). | ||||
|  | ||||
| **When I watch a directory, are all subdirectories watched as well?** | ||||
|  | ||||
| No, you must add watches for any directory you want to watch (a recursive watcher is on the roadmap [#18][]). | ||||
|  | ||||
| **Do I have to watch the Error and Event channels in a separate goroutine?** | ||||
|  | ||||
| As of now, yes. Looking into making this single-thread friendly (see [howeyc #7][#7]) | ||||
|  | ||||
| **Why am I receiving multiple events for the same file on OS X?** | ||||
|  | ||||
| Spotlight indexing on OS X can result in multiple events (see [howeyc #62][#62]). A temporary workaround is to add your folder(s) to the *Spotlight Privacy settings* until we have a native FSEvents implementation (see [#11][]). | ||||
|  | ||||
| **How many files can be watched at once?** | ||||
|  | ||||
| There are OS-specific limits as to how many watches can be created: | ||||
| * Linux: /proc/sys/fs/inotify/max_user_watches contains the limit, reaching this limit results in a "no space left on device" error. | ||||
| * BSD / OSX: sysctl variables "kern.maxfiles" and "kern.maxfilesperproc", reaching these limits results in a "too many open files" error. | ||||
|  | ||||
| [#62]: https://github.com/howeyc/fsnotify/issues/62 | ||||
| [#18]: https://github.com/fsnotify/fsnotify/issues/18 | ||||
| [#11]: https://github.com/fsnotify/fsnotify/issues/11 | ||||
| [#7]: https://github.com/howeyc/fsnotify/issues/7 | ||||
|  | ||||
| [contributing]: https://github.com/fsnotify/fsnotify/blob/master/CONTRIBUTING.md | ||||
|  | ||||
| ## Related Projects | ||||
|  | ||||
| * [notify](https://github.com/rjeczalik/notify) | ||||
| * [fsevents](https://github.com/fsnotify/fsevents) | ||||
|  | ||||
							
								
								
									
										37
									
								
								vendor/github.com/fsnotify/fsnotify/fen.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										37
									
								
								vendor/github.com/fsnotify/fsnotify/fen.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,37 +0,0 @@ | ||||
| // Copyright 2010 The Go Authors. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| // +build solaris | ||||
|  | ||||
| package fsnotify | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| ) | ||||
|  | ||||
| // Watcher watches a set of files, delivering events to a channel. | ||||
| type Watcher struct { | ||||
| 	Events chan Event | ||||
| 	Errors chan error | ||||
| } | ||||
|  | ||||
| // NewWatcher establishes a new watcher with the underlying OS and begins waiting for events. | ||||
| func NewWatcher() (*Watcher, error) { | ||||
| 	return nil, errors.New("FEN based watcher not yet supported for fsnotify\n") | ||||
| } | ||||
|  | ||||
| // Close removes all watches and closes the events channel. | ||||
| func (w *Watcher) Close() error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Add starts watching the named file or directory (non-recursively). | ||||
| func (w *Watcher) Add(name string) error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Remove stops watching the the named file or directory (non-recursively). | ||||
| func (w *Watcher) Remove(name string) error { | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										66
									
								
								vendor/github.com/fsnotify/fsnotify/fsnotify.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										66
									
								
								vendor/github.com/fsnotify/fsnotify/fsnotify.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,66 +0,0 @@ | ||||
| // Copyright 2012 The Go Authors. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| // +build !plan9 | ||||
|  | ||||
| // Package fsnotify provides a platform-independent interface for file system notifications. | ||||
| package fsnotify | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| ) | ||||
|  | ||||
| // Event represents a single file system notification. | ||||
| type Event struct { | ||||
| 	Name string // Relative path to the file or directory. | ||||
| 	Op   Op     // File operation that triggered the event. | ||||
| } | ||||
|  | ||||
| // Op describes a set of file operations. | ||||
| type Op uint32 | ||||
|  | ||||
| // These are the generalized file operations that can trigger a notification. | ||||
| const ( | ||||
| 	Create Op = 1 << iota | ||||
| 	Write | ||||
| 	Remove | ||||
| 	Rename | ||||
| 	Chmod | ||||
| ) | ||||
|  | ||||
| func (op Op) String() string { | ||||
| 	// Use a buffer for efficient string concatenation | ||||
| 	var buffer bytes.Buffer | ||||
|  | ||||
| 	if op&Create == Create { | ||||
| 		buffer.WriteString("|CREATE") | ||||
| 	} | ||||
| 	if op&Remove == Remove { | ||||
| 		buffer.WriteString("|REMOVE") | ||||
| 	} | ||||
| 	if op&Write == Write { | ||||
| 		buffer.WriteString("|WRITE") | ||||
| 	} | ||||
| 	if op&Rename == Rename { | ||||
| 		buffer.WriteString("|RENAME") | ||||
| 	} | ||||
| 	if op&Chmod == Chmod { | ||||
| 		buffer.WriteString("|CHMOD") | ||||
| 	} | ||||
| 	if buffer.Len() == 0 { | ||||
| 		return "" | ||||
| 	} | ||||
| 	return buffer.String()[1:] // Strip leading pipe | ||||
| } | ||||
|  | ||||
| // String returns a string representation of the event in the form | ||||
| // "file: REMOVE|WRITE|..." | ||||
| func (e Event) String() string { | ||||
| 	return fmt.Sprintf("%q: %s", e.Name, e.Op.String()) | ||||
| } | ||||
|  | ||||
| // Common errors that can be reported by a watcher | ||||
| var ErrEventOverflow = errors.New("fsnotify queue overflow") | ||||
							
								
								
									
										337
									
								
								vendor/github.com/fsnotify/fsnotify/inotify.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										337
									
								
								vendor/github.com/fsnotify/fsnotify/inotify.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,337 +0,0 @@ | ||||
| // Copyright 2010 The Go Authors. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| // +build linux | ||||
|  | ||||
| package fsnotify | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 	"unsafe" | ||||
|  | ||||
| 	"golang.org/x/sys/unix" | ||||
| ) | ||||
|  | ||||
| // Watcher watches a set of files, delivering events to a channel. | ||||
| type Watcher struct { | ||||
| 	Events   chan Event | ||||
| 	Errors   chan error | ||||
| 	mu       sync.Mutex // Map access | ||||
| 	fd       int | ||||
| 	poller   *fdPoller | ||||
| 	watches  map[string]*watch // Map of inotify watches (key: path) | ||||
| 	paths    map[int]string    // Map of watched paths (key: watch descriptor) | ||||
| 	done     chan struct{}     // Channel for sending a "quit message" to the reader goroutine | ||||
| 	doneResp chan struct{}     // Channel to respond to Close | ||||
| } | ||||
|  | ||||
| // NewWatcher establishes a new watcher with the underlying OS and begins waiting for events. | ||||
| func NewWatcher() (*Watcher, error) { | ||||
| 	// Create inotify fd | ||||
| 	fd, errno := unix.InotifyInit1(unix.IN_CLOEXEC) | ||||
| 	if fd == -1 { | ||||
| 		return nil, errno | ||||
| 	} | ||||
| 	// Create epoll | ||||
| 	poller, err := newFdPoller(fd) | ||||
| 	if err != nil { | ||||
| 		unix.Close(fd) | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	w := &Watcher{ | ||||
| 		fd:       fd, | ||||
| 		poller:   poller, | ||||
| 		watches:  make(map[string]*watch), | ||||
| 		paths:    make(map[int]string), | ||||
| 		Events:   make(chan Event), | ||||
| 		Errors:   make(chan error), | ||||
| 		done:     make(chan struct{}), | ||||
| 		doneResp: make(chan struct{}), | ||||
| 	} | ||||
|  | ||||
| 	go w.readEvents() | ||||
| 	return w, nil | ||||
| } | ||||
|  | ||||
| func (w *Watcher) isClosed() bool { | ||||
| 	select { | ||||
| 	case <-w.done: | ||||
| 		return true | ||||
| 	default: | ||||
| 		return false | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Close removes all watches and closes the events channel. | ||||
| func (w *Watcher) Close() error { | ||||
| 	if w.isClosed() { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	// Send 'close' signal to goroutine, and set the Watcher to closed. | ||||
| 	close(w.done) | ||||
|  | ||||
| 	// Wake up goroutine | ||||
| 	w.poller.wake() | ||||
|  | ||||
| 	// Wait for goroutine to close | ||||
| 	<-w.doneResp | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Add starts watching the named file or directory (non-recursively). | ||||
| func (w *Watcher) Add(name string) error { | ||||
| 	name = filepath.Clean(name) | ||||
| 	if w.isClosed() { | ||||
| 		return errors.New("inotify instance already closed") | ||||
| 	} | ||||
|  | ||||
| 	const agnosticEvents = unix.IN_MOVED_TO | unix.IN_MOVED_FROM | | ||||
| 		unix.IN_CREATE | unix.IN_ATTRIB | unix.IN_MODIFY | | ||||
| 		unix.IN_MOVE_SELF | unix.IN_DELETE | unix.IN_DELETE_SELF | ||||
|  | ||||
| 	var flags uint32 = agnosticEvents | ||||
|  | ||||
| 	w.mu.Lock() | ||||
| 	defer w.mu.Unlock() | ||||
| 	watchEntry := w.watches[name] | ||||
| 	if watchEntry != nil { | ||||
| 		flags |= watchEntry.flags | unix.IN_MASK_ADD | ||||
| 	} | ||||
| 	wd, errno := unix.InotifyAddWatch(w.fd, name, flags) | ||||
| 	if wd == -1 { | ||||
| 		return errno | ||||
| 	} | ||||
|  | ||||
| 	if watchEntry == nil { | ||||
| 		w.watches[name] = &watch{wd: uint32(wd), flags: flags} | ||||
| 		w.paths[wd] = name | ||||
| 	} else { | ||||
| 		watchEntry.wd = uint32(wd) | ||||
| 		watchEntry.flags = flags | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Remove stops watching the named file or directory (non-recursively). | ||||
| func (w *Watcher) Remove(name string) error { | ||||
| 	name = filepath.Clean(name) | ||||
|  | ||||
| 	// Fetch the watch. | ||||
| 	w.mu.Lock() | ||||
| 	defer w.mu.Unlock() | ||||
| 	watch, ok := w.watches[name] | ||||
|  | ||||
| 	// Remove it from inotify. | ||||
| 	if !ok { | ||||
| 		return fmt.Errorf("can't remove non-existent inotify watch for: %s", name) | ||||
| 	} | ||||
|  | ||||
| 	// We successfully removed the watch if InotifyRmWatch doesn't return an | ||||
| 	// error, we need to clean up our internal state to ensure it matches | ||||
| 	// inotify's kernel state. | ||||
| 	delete(w.paths, int(watch.wd)) | ||||
| 	delete(w.watches, name) | ||||
|  | ||||
| 	// inotify_rm_watch will return EINVAL if the file has been deleted; | ||||
| 	// the inotify will already have been removed. | ||||
| 	// watches and pathes are deleted in ignoreLinux() implicitly and asynchronously | ||||
| 	// by calling inotify_rm_watch() below. e.g. readEvents() goroutine receives IN_IGNORE | ||||
| 	// so that EINVAL means that the wd is being rm_watch()ed or its file removed | ||||
| 	// by another thread and we have not received IN_IGNORE event. | ||||
| 	success, errno := unix.InotifyRmWatch(w.fd, watch.wd) | ||||
| 	if success == -1 { | ||||
| 		// TODO: Perhaps it's not helpful to return an error here in every case. | ||||
| 		// the only two possible errors are: | ||||
| 		// EBADF, which happens when w.fd is not a valid file descriptor of any kind. | ||||
| 		// EINVAL, which is when fd is not an inotify descriptor or wd is not a valid watch descriptor. | ||||
| 		// Watch descriptors are invalidated when they are removed explicitly or implicitly; | ||||
| 		// explicitly by inotify_rm_watch, implicitly when the file they are watching is deleted. | ||||
| 		return errno | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| type watch struct { | ||||
| 	wd    uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall) | ||||
| 	flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags) | ||||
| } | ||||
|  | ||||
| // readEvents reads from the inotify file descriptor, converts the | ||||
| // received events into Event objects and sends them via the Events channel | ||||
| func (w *Watcher) readEvents() { | ||||
| 	var ( | ||||
| 		buf   [unix.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events | ||||
| 		n     int                                  // Number of bytes read with read() | ||||
| 		errno error                                // Syscall errno | ||||
| 		ok    bool                                 // For poller.wait | ||||
| 	) | ||||
|  | ||||
| 	defer close(w.doneResp) | ||||
| 	defer close(w.Errors) | ||||
| 	defer close(w.Events) | ||||
| 	defer unix.Close(w.fd) | ||||
| 	defer w.poller.close() | ||||
|  | ||||
| 	for { | ||||
| 		// See if we have been closed. | ||||
| 		if w.isClosed() { | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		ok, errno = w.poller.wait() | ||||
| 		if errno != nil { | ||||
| 			select { | ||||
| 			case w.Errors <- errno: | ||||
| 			case <-w.done: | ||||
| 				return | ||||
| 			} | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		if !ok { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		n, errno = unix.Read(w.fd, buf[:]) | ||||
| 		// If a signal interrupted execution, see if we've been asked to close, and try again. | ||||
| 		// http://man7.org/linux/man-pages/man7/signal.7.html : | ||||
| 		// "Before Linux 3.8, reads from an inotify(7) file descriptor were not restartable" | ||||
| 		if errno == unix.EINTR { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		// unix.Read might have been woken up by Close. If so, we're done. | ||||
| 		if w.isClosed() { | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		if n < unix.SizeofInotifyEvent { | ||||
| 			var err error | ||||
| 			if n == 0 { | ||||
| 				// If EOF is received. This should really never happen. | ||||
| 				err = io.EOF | ||||
| 			} else if n < 0 { | ||||
| 				// If an error occurred while reading. | ||||
| 				err = errno | ||||
| 			} else { | ||||
| 				// Read was too short. | ||||
| 				err = errors.New("notify: short read in readEvents()") | ||||
| 			} | ||||
| 			select { | ||||
| 			case w.Errors <- err: | ||||
| 			case <-w.done: | ||||
| 				return | ||||
| 			} | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		var offset uint32 | ||||
| 		// We don't know how many events we just read into the buffer | ||||
| 		// While the offset points to at least one whole event... | ||||
| 		for offset <= uint32(n-unix.SizeofInotifyEvent) { | ||||
| 			// Point "raw" to the event in the buffer | ||||
| 			raw := (*unix.InotifyEvent)(unsafe.Pointer(&buf[offset])) | ||||
|  | ||||
| 			mask := uint32(raw.Mask) | ||||
| 			nameLen := uint32(raw.Len) | ||||
|  | ||||
| 			if mask&unix.IN_Q_OVERFLOW != 0 { | ||||
| 				select { | ||||
| 				case w.Errors <- ErrEventOverflow: | ||||
| 				case <-w.done: | ||||
| 					return | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			// If the event happened to the watched directory or the watched file, the kernel | ||||
| 			// doesn't append the filename to the event, but we would like to always fill the | ||||
| 			// the "Name" field with a valid filename. We retrieve the path of the watch from | ||||
| 			// the "paths" map. | ||||
| 			w.mu.Lock() | ||||
| 			name, ok := w.paths[int(raw.Wd)] | ||||
| 			// IN_DELETE_SELF occurs when the file/directory being watched is removed. | ||||
| 			// This is a sign to clean up the maps, otherwise we are no longer in sync | ||||
| 			// with the inotify kernel state which has already deleted the watch | ||||
| 			// automatically. | ||||
| 			if ok && mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF { | ||||
| 				delete(w.paths, int(raw.Wd)) | ||||
| 				delete(w.watches, name) | ||||
| 			} | ||||
| 			w.mu.Unlock() | ||||
|  | ||||
| 			if nameLen > 0 { | ||||
| 				// Point "bytes" at the first byte of the filename | ||||
| 				bytes := (*[unix.PathMax]byte)(unsafe.Pointer(&buf[offset+unix.SizeofInotifyEvent])) | ||||
| 				// The filename is padded with NULL bytes. TrimRight() gets rid of those. | ||||
| 				name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\000") | ||||
| 			} | ||||
|  | ||||
| 			event := newEvent(name, mask) | ||||
|  | ||||
| 			// Send the events that are not ignored on the events channel | ||||
| 			if !event.ignoreLinux(mask) { | ||||
| 				select { | ||||
| 				case w.Events <- event: | ||||
| 				case <-w.done: | ||||
| 					return | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			// Move to the next event in the buffer | ||||
| 			offset += unix.SizeofInotifyEvent + nameLen | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Certain types of events can be "ignored" and not sent over the Events | ||||
| // channel. Such as events marked ignore by the kernel, or MODIFY events | ||||
| // against files that do not exist. | ||||
| func (e *Event) ignoreLinux(mask uint32) bool { | ||||
| 	// Ignore anything the inotify API says to ignore | ||||
| 	if mask&unix.IN_IGNORED == unix.IN_IGNORED { | ||||
| 		return true | ||||
| 	} | ||||
|  | ||||
| 	// If the event is not a DELETE or RENAME, the file must exist. | ||||
| 	// Otherwise the event is ignored. | ||||
| 	// *Note*: this was put in place because it was seen that a MODIFY | ||||
| 	// event was sent after the DELETE. This ignores that MODIFY and | ||||
| 	// assumes a DELETE will come or has come if the file doesn't exist. | ||||
| 	if !(e.Op&Remove == Remove || e.Op&Rename == Rename) { | ||||
| 		_, statErr := os.Lstat(e.Name) | ||||
| 		return os.IsNotExist(statErr) | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| // newEvent returns an platform-independent Event based on an inotify mask. | ||||
| func newEvent(name string, mask uint32) Event { | ||||
| 	e := Event{Name: name} | ||||
| 	if mask&unix.IN_CREATE == unix.IN_CREATE || mask&unix.IN_MOVED_TO == unix.IN_MOVED_TO { | ||||
| 		e.Op |= Create | ||||
| 	} | ||||
| 	if mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF || mask&unix.IN_DELETE == unix.IN_DELETE { | ||||
| 		e.Op |= Remove | ||||
| 	} | ||||
| 	if mask&unix.IN_MODIFY == unix.IN_MODIFY { | ||||
| 		e.Op |= Write | ||||
| 	} | ||||
| 	if mask&unix.IN_MOVE_SELF == unix.IN_MOVE_SELF || mask&unix.IN_MOVED_FROM == unix.IN_MOVED_FROM { | ||||
| 		e.Op |= Rename | ||||
| 	} | ||||
| 	if mask&unix.IN_ATTRIB == unix.IN_ATTRIB { | ||||
| 		e.Op |= Chmod | ||||
| 	} | ||||
| 	return e | ||||
| } | ||||
							
								
								
									
										187
									
								
								vendor/github.com/fsnotify/fsnotify/inotify_poller.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										187
									
								
								vendor/github.com/fsnotify/fsnotify/inotify_poller.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,187 +0,0 @@ | ||||
| // Copyright 2015 The Go Authors. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| // +build linux | ||||
|  | ||||
| package fsnotify | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
|  | ||||
| 	"golang.org/x/sys/unix" | ||||
| ) | ||||
|  | ||||
| type fdPoller struct { | ||||
| 	fd   int    // File descriptor (as returned by the inotify_init() syscall) | ||||
| 	epfd int    // Epoll file descriptor | ||||
| 	pipe [2]int // Pipe for waking up | ||||
| } | ||||
|  | ||||
| func emptyPoller(fd int) *fdPoller { | ||||
| 	poller := new(fdPoller) | ||||
| 	poller.fd = fd | ||||
| 	poller.epfd = -1 | ||||
| 	poller.pipe[0] = -1 | ||||
| 	poller.pipe[1] = -1 | ||||
| 	return poller | ||||
| } | ||||
|  | ||||
| // Create a new inotify poller. | ||||
| // This creates an inotify handler, and an epoll handler. | ||||
| func newFdPoller(fd int) (*fdPoller, error) { | ||||
| 	var errno error | ||||
| 	poller := emptyPoller(fd) | ||||
| 	defer func() { | ||||
| 		if errno != nil { | ||||
| 			poller.close() | ||||
| 		} | ||||
| 	}() | ||||
| 	poller.fd = fd | ||||
|  | ||||
| 	// Create epoll fd | ||||
| 	poller.epfd, errno = unix.EpollCreate1(0) | ||||
| 	if poller.epfd == -1 { | ||||
| 		return nil, errno | ||||
| 	} | ||||
| 	// Create pipe; pipe[0] is the read end, pipe[1] the write end. | ||||
| 	errno = unix.Pipe2(poller.pipe[:], unix.O_NONBLOCK) | ||||
| 	if errno != nil { | ||||
| 		return nil, errno | ||||
| 	} | ||||
|  | ||||
| 	// Register inotify fd with epoll | ||||
| 	event := unix.EpollEvent{ | ||||
| 		Fd:     int32(poller.fd), | ||||
| 		Events: unix.EPOLLIN, | ||||
| 	} | ||||
| 	errno = unix.EpollCtl(poller.epfd, unix.EPOLL_CTL_ADD, poller.fd, &event) | ||||
| 	if errno != nil { | ||||
| 		return nil, errno | ||||
| 	} | ||||
|  | ||||
| 	// Register pipe fd with epoll | ||||
| 	event = unix.EpollEvent{ | ||||
| 		Fd:     int32(poller.pipe[0]), | ||||
| 		Events: unix.EPOLLIN, | ||||
| 	} | ||||
| 	errno = unix.EpollCtl(poller.epfd, unix.EPOLL_CTL_ADD, poller.pipe[0], &event) | ||||
| 	if errno != nil { | ||||
| 		return nil, errno | ||||
| 	} | ||||
|  | ||||
| 	return poller, nil | ||||
| } | ||||
|  | ||||
| // Wait using epoll. | ||||
| // Returns true if something is ready to be read, | ||||
| // false if there is not. | ||||
| func (poller *fdPoller) wait() (bool, error) { | ||||
| 	// 3 possible events per fd, and 2 fds, makes a maximum of 6 events. | ||||
| 	// I don't know whether epoll_wait returns the number of events returned, | ||||
| 	// or the total number of events ready. | ||||
| 	// I decided to catch both by making the buffer one larger than the maximum. | ||||
| 	events := make([]unix.EpollEvent, 7) | ||||
| 	for { | ||||
| 		n, errno := unix.EpollWait(poller.epfd, events, -1) | ||||
| 		if n == -1 { | ||||
| 			if errno == unix.EINTR { | ||||
| 				continue | ||||
| 			} | ||||
| 			return false, errno | ||||
| 		} | ||||
| 		if n == 0 { | ||||
| 			// If there are no events, try again. | ||||
| 			continue | ||||
| 		} | ||||
| 		if n > 6 { | ||||
| 			// This should never happen. More events were returned than should be possible. | ||||
| 			return false, errors.New("epoll_wait returned more events than I know what to do with") | ||||
| 		} | ||||
| 		ready := events[:n] | ||||
| 		epollhup := false | ||||
| 		epollerr := false | ||||
| 		epollin := false | ||||
| 		for _, event := range ready { | ||||
| 			if event.Fd == int32(poller.fd) { | ||||
| 				if event.Events&unix.EPOLLHUP != 0 { | ||||
| 					// This should not happen, but if it does, treat it as a wakeup. | ||||
| 					epollhup = true | ||||
| 				} | ||||
| 				if event.Events&unix.EPOLLERR != 0 { | ||||
| 					// If an error is waiting on the file descriptor, we should pretend | ||||
| 					// something is ready to read, and let unix.Read pick up the error. | ||||
| 					epollerr = true | ||||
| 				} | ||||
| 				if event.Events&unix.EPOLLIN != 0 { | ||||
| 					// There is data to read. | ||||
| 					epollin = true | ||||
| 				} | ||||
| 			} | ||||
| 			if event.Fd == int32(poller.pipe[0]) { | ||||
| 				if event.Events&unix.EPOLLHUP != 0 { | ||||
| 					// Write pipe descriptor was closed, by us. This means we're closing down the | ||||
| 					// watcher, and we should wake up. | ||||
| 				} | ||||
| 				if event.Events&unix.EPOLLERR != 0 { | ||||
| 					// If an error is waiting on the pipe file descriptor. | ||||
| 					// This is an absolute mystery, and should never ever happen. | ||||
| 					return false, errors.New("Error on the pipe descriptor.") | ||||
| 				} | ||||
| 				if event.Events&unix.EPOLLIN != 0 { | ||||
| 					// This is a regular wakeup, so we have to clear the buffer. | ||||
| 					err := poller.clearWake() | ||||
| 					if err != nil { | ||||
| 						return false, err | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if epollhup || epollerr || epollin { | ||||
| 			return true, nil | ||||
| 		} | ||||
| 		return false, nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Close the write end of the poller. | ||||
| func (poller *fdPoller) wake() error { | ||||
| 	buf := make([]byte, 1) | ||||
| 	n, errno := unix.Write(poller.pipe[1], buf) | ||||
| 	if n == -1 { | ||||
| 		if errno == unix.EAGAIN { | ||||
| 			// Buffer is full, poller will wake. | ||||
| 			return nil | ||||
| 		} | ||||
| 		return errno | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (poller *fdPoller) clearWake() error { | ||||
| 	// You have to be woken up a LOT in order to get to 100! | ||||
| 	buf := make([]byte, 100) | ||||
| 	n, errno := unix.Read(poller.pipe[0], buf) | ||||
| 	if n == -1 { | ||||
| 		if errno == unix.EAGAIN { | ||||
| 			// Buffer is empty, someone else cleared our wake. | ||||
| 			return nil | ||||
| 		} | ||||
| 		return errno | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Close all poller file descriptors, but not the one passed to it. | ||||
| func (poller *fdPoller) close() { | ||||
| 	if poller.pipe[1] != -1 { | ||||
| 		unix.Close(poller.pipe[1]) | ||||
| 	} | ||||
| 	if poller.pipe[0] != -1 { | ||||
| 		unix.Close(poller.pipe[0]) | ||||
| 	} | ||||
| 	if poller.epfd != -1 { | ||||
| 		unix.Close(poller.epfd) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										521
									
								
								vendor/github.com/fsnotify/fsnotify/kqueue.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										521
									
								
								vendor/github.com/fsnotify/fsnotify/kqueue.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,521 +0,0 @@ | ||||
| // Copyright 2010 The Go Authors. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| // +build freebsd openbsd netbsd dragonfly darwin | ||||
|  | ||||
| package fsnotify | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"sync" | ||||
| 	"time" | ||||
|  | ||||
| 	"golang.org/x/sys/unix" | ||||
| ) | ||||
|  | ||||
| // Watcher watches a set of files, delivering events to a channel. | ||||
| type Watcher struct { | ||||
| 	Events chan Event | ||||
| 	Errors chan error | ||||
| 	done   chan struct{} // Channel for sending a "quit message" to the reader goroutine | ||||
|  | ||||
| 	kq int // File descriptor (as returned by the kqueue() syscall). | ||||
|  | ||||
| 	mu              sync.Mutex        // Protects access to watcher data | ||||
| 	watches         map[string]int    // Map of watched file descriptors (key: path). | ||||
| 	externalWatches map[string]bool   // Map of watches added by user of the library. | ||||
| 	dirFlags        map[string]uint32 // Map of watched directories to fflags used in kqueue. | ||||
| 	paths           map[int]pathInfo  // Map file descriptors to path names for processing kqueue events. | ||||
| 	fileExists      map[string]bool   // Keep track of if we know this file exists (to stop duplicate create events). | ||||
| 	isClosed        bool              // Set to true when Close() is first called | ||||
| } | ||||
|  | ||||
| type pathInfo struct { | ||||
| 	name  string | ||||
| 	isDir bool | ||||
| } | ||||
|  | ||||
| // NewWatcher establishes a new watcher with the underlying OS and begins waiting for events. | ||||
| func NewWatcher() (*Watcher, error) { | ||||
| 	kq, err := kqueue() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	w := &Watcher{ | ||||
| 		kq:              kq, | ||||
| 		watches:         make(map[string]int), | ||||
| 		dirFlags:        make(map[string]uint32), | ||||
| 		paths:           make(map[int]pathInfo), | ||||
| 		fileExists:      make(map[string]bool), | ||||
| 		externalWatches: make(map[string]bool), | ||||
| 		Events:          make(chan Event), | ||||
| 		Errors:          make(chan error), | ||||
| 		done:            make(chan struct{}), | ||||
| 	} | ||||
|  | ||||
| 	go w.readEvents() | ||||
| 	return w, nil | ||||
| } | ||||
|  | ||||
| // Close removes all watches and closes the events channel. | ||||
| func (w *Watcher) Close() error { | ||||
| 	w.mu.Lock() | ||||
| 	if w.isClosed { | ||||
| 		w.mu.Unlock() | ||||
| 		return nil | ||||
| 	} | ||||
| 	w.isClosed = true | ||||
|  | ||||
| 	// copy paths to remove while locked | ||||
| 	var pathsToRemove = make([]string, 0, len(w.watches)) | ||||
| 	for name := range w.watches { | ||||
| 		pathsToRemove = append(pathsToRemove, name) | ||||
| 	} | ||||
| 	w.mu.Unlock() | ||||
| 	// unlock before calling Remove, which also locks | ||||
|  | ||||
| 	for _, name := range pathsToRemove { | ||||
| 		w.Remove(name) | ||||
| 	} | ||||
|  | ||||
| 	// send a "quit" message to the reader goroutine | ||||
| 	close(w.done) | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Add starts watching the named file or directory (non-recursively). | ||||
| func (w *Watcher) Add(name string) error { | ||||
| 	w.mu.Lock() | ||||
| 	w.externalWatches[name] = true | ||||
| 	w.mu.Unlock() | ||||
| 	_, err := w.addWatch(name, noteAllEvents) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| // Remove stops watching the the named file or directory (non-recursively). | ||||
| func (w *Watcher) Remove(name string) error { | ||||
| 	name = filepath.Clean(name) | ||||
| 	w.mu.Lock() | ||||
| 	watchfd, ok := w.watches[name] | ||||
| 	w.mu.Unlock() | ||||
| 	if !ok { | ||||
| 		return fmt.Errorf("can't remove non-existent kevent watch for: %s", name) | ||||
| 	} | ||||
|  | ||||
| 	const registerRemove = unix.EV_DELETE | ||||
| 	if err := register(w.kq, []int{watchfd}, registerRemove, 0); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	unix.Close(watchfd) | ||||
|  | ||||
| 	w.mu.Lock() | ||||
| 	isDir := w.paths[watchfd].isDir | ||||
| 	delete(w.watches, name) | ||||
| 	delete(w.paths, watchfd) | ||||
| 	delete(w.dirFlags, name) | ||||
| 	w.mu.Unlock() | ||||
|  | ||||
| 	// Find all watched paths that are in this directory that are not external. | ||||
| 	if isDir { | ||||
| 		var pathsToRemove []string | ||||
| 		w.mu.Lock() | ||||
| 		for _, path := range w.paths { | ||||
| 			wdir, _ := filepath.Split(path.name) | ||||
| 			if filepath.Clean(wdir) == name { | ||||
| 				if !w.externalWatches[path.name] { | ||||
| 					pathsToRemove = append(pathsToRemove, path.name) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		w.mu.Unlock() | ||||
| 		for _, name := range pathsToRemove { | ||||
| 			// Since these are internal, not much sense in propagating error | ||||
| 			// to the user, as that will just confuse them with an error about | ||||
| 			// a path they did not explicitly watch themselves. | ||||
| 			w.Remove(name) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Watch all events (except NOTE_EXTEND, NOTE_LINK, NOTE_REVOKE) | ||||
| const noteAllEvents = unix.NOTE_DELETE | unix.NOTE_WRITE | unix.NOTE_ATTRIB | unix.NOTE_RENAME | ||||
|  | ||||
| // keventWaitTime to block on each read from kevent | ||||
| var keventWaitTime = durationToTimespec(100 * time.Millisecond) | ||||
|  | ||||
| // addWatch adds name to the watched file set. | ||||
| // The flags are interpreted as described in kevent(2). | ||||
| // Returns the real path to the file which was added, if any, which may be different from the one passed in the case of symlinks. | ||||
| func (w *Watcher) addWatch(name string, flags uint32) (string, error) { | ||||
| 	var isDir bool | ||||
| 	// Make ./name and name equivalent | ||||
| 	name = filepath.Clean(name) | ||||
|  | ||||
| 	w.mu.Lock() | ||||
| 	if w.isClosed { | ||||
| 		w.mu.Unlock() | ||||
| 		return "", errors.New("kevent instance already closed") | ||||
| 	} | ||||
| 	watchfd, alreadyWatching := w.watches[name] | ||||
| 	// We already have a watch, but we can still override flags. | ||||
| 	if alreadyWatching { | ||||
| 		isDir = w.paths[watchfd].isDir | ||||
| 	} | ||||
| 	w.mu.Unlock() | ||||
|  | ||||
| 	if !alreadyWatching { | ||||
| 		fi, err := os.Lstat(name) | ||||
| 		if err != nil { | ||||
| 			return "", err | ||||
| 		} | ||||
|  | ||||
| 		// Don't watch sockets. | ||||
| 		if fi.Mode()&os.ModeSocket == os.ModeSocket { | ||||
| 			return "", nil | ||||
| 		} | ||||
|  | ||||
| 		// Don't watch named pipes. | ||||
| 		if fi.Mode()&os.ModeNamedPipe == os.ModeNamedPipe { | ||||
| 			return "", nil | ||||
| 		} | ||||
|  | ||||
| 		// Follow Symlinks | ||||
| 		// Unfortunately, Linux can add bogus symlinks to watch list without | ||||
| 		// issue, and Windows can't do symlinks period (AFAIK). To  maintain | ||||
| 		// consistency, we will act like everything is fine. There will simply | ||||
| 		// be no file events for broken symlinks. | ||||
| 		// Hence the returns of nil on errors. | ||||
| 		if fi.Mode()&os.ModeSymlink == os.ModeSymlink { | ||||
| 			name, err = filepath.EvalSymlinks(name) | ||||
| 			if err != nil { | ||||
| 				return "", nil | ||||
| 			} | ||||
|  | ||||
| 			w.mu.Lock() | ||||
| 			_, alreadyWatching = w.watches[name] | ||||
| 			w.mu.Unlock() | ||||
|  | ||||
| 			if alreadyWatching { | ||||
| 				return name, nil | ||||
| 			} | ||||
|  | ||||
| 			fi, err = os.Lstat(name) | ||||
| 			if err != nil { | ||||
| 				return "", nil | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		watchfd, err = unix.Open(name, openMode, 0700) | ||||
| 		if watchfd == -1 { | ||||
| 			return "", err | ||||
| 		} | ||||
|  | ||||
| 		isDir = fi.IsDir() | ||||
| 	} | ||||
|  | ||||
| 	const registerAdd = unix.EV_ADD | unix.EV_CLEAR | unix.EV_ENABLE | ||||
| 	if err := register(w.kq, []int{watchfd}, registerAdd, flags); err != nil { | ||||
| 		unix.Close(watchfd) | ||||
| 		return "", err | ||||
| 	} | ||||
|  | ||||
| 	if !alreadyWatching { | ||||
| 		w.mu.Lock() | ||||
| 		w.watches[name] = watchfd | ||||
| 		w.paths[watchfd] = pathInfo{name: name, isDir: isDir} | ||||
| 		w.mu.Unlock() | ||||
| 	} | ||||
|  | ||||
| 	if isDir { | ||||
| 		// Watch the directory if it has not been watched before, | ||||
| 		// or if it was watched before, but perhaps only a NOTE_DELETE (watchDirectoryFiles) | ||||
| 		w.mu.Lock() | ||||
|  | ||||
| 		watchDir := (flags&unix.NOTE_WRITE) == unix.NOTE_WRITE && | ||||
| 			(!alreadyWatching || (w.dirFlags[name]&unix.NOTE_WRITE) != unix.NOTE_WRITE) | ||||
| 		// Store flags so this watch can be updated later | ||||
| 		w.dirFlags[name] = flags | ||||
| 		w.mu.Unlock() | ||||
|  | ||||
| 		if watchDir { | ||||
| 			if err := w.watchDirectoryFiles(name); err != nil { | ||||
| 				return "", err | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return name, nil | ||||
| } | ||||
|  | ||||
| // readEvents reads from kqueue and converts the received kevents into | ||||
| // Event values that it sends down the Events channel. | ||||
| func (w *Watcher) readEvents() { | ||||
| 	eventBuffer := make([]unix.Kevent_t, 10) | ||||
|  | ||||
| loop: | ||||
| 	for { | ||||
| 		// See if there is a message on the "done" channel | ||||
| 		select { | ||||
| 		case <-w.done: | ||||
| 			break loop | ||||
| 		default: | ||||
| 		} | ||||
|  | ||||
| 		// Get new events | ||||
| 		kevents, err := read(w.kq, eventBuffer, &keventWaitTime) | ||||
| 		// EINTR is okay, the syscall was interrupted before timeout expired. | ||||
| 		if err != nil && err != unix.EINTR { | ||||
| 			select { | ||||
| 			case w.Errors <- err: | ||||
| 			case <-w.done: | ||||
| 				break loop | ||||
| 			} | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		// Flush the events we received to the Events channel | ||||
| 		for len(kevents) > 0 { | ||||
| 			kevent := &kevents[0] | ||||
| 			watchfd := int(kevent.Ident) | ||||
| 			mask := uint32(kevent.Fflags) | ||||
| 			w.mu.Lock() | ||||
| 			path := w.paths[watchfd] | ||||
| 			w.mu.Unlock() | ||||
| 			event := newEvent(path.name, mask) | ||||
|  | ||||
| 			if path.isDir && !(event.Op&Remove == Remove) { | ||||
| 				// Double check to make sure the directory exists. This can happen when | ||||
| 				// we do a rm -fr on a recursively watched folders and we receive a | ||||
| 				// modification event first but the folder has been deleted and later | ||||
| 				// receive the delete event | ||||
| 				if _, err := os.Lstat(event.Name); os.IsNotExist(err) { | ||||
| 					// mark is as delete event | ||||
| 					event.Op |= Remove | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			if event.Op&Rename == Rename || event.Op&Remove == Remove { | ||||
| 				w.Remove(event.Name) | ||||
| 				w.mu.Lock() | ||||
| 				delete(w.fileExists, event.Name) | ||||
| 				w.mu.Unlock() | ||||
| 			} | ||||
|  | ||||
| 			if path.isDir && event.Op&Write == Write && !(event.Op&Remove == Remove) { | ||||
| 				w.sendDirectoryChangeEvents(event.Name) | ||||
| 			} else { | ||||
| 				// Send the event on the Events channel. | ||||
| 				select { | ||||
| 				case w.Events <- event: | ||||
| 				case <-w.done: | ||||
| 					break loop | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			if event.Op&Remove == Remove { | ||||
| 				// Look for a file that may have overwritten this. | ||||
| 				// For example, mv f1 f2 will delete f2, then create f2. | ||||
| 				if path.isDir { | ||||
| 					fileDir := filepath.Clean(event.Name) | ||||
| 					w.mu.Lock() | ||||
| 					_, found := w.watches[fileDir] | ||||
| 					w.mu.Unlock() | ||||
| 					if found { | ||||
| 						// make sure the directory exists before we watch for changes. When we | ||||
| 						// do a recursive watch and perform rm -fr, the parent directory might | ||||
| 						// have gone missing, ignore the missing directory and let the | ||||
| 						// upcoming delete event remove the watch from the parent directory. | ||||
| 						if _, err := os.Lstat(fileDir); err == nil { | ||||
| 							w.sendDirectoryChangeEvents(fileDir) | ||||
| 						} | ||||
| 					} | ||||
| 				} else { | ||||
| 					filePath := filepath.Clean(event.Name) | ||||
| 					if fileInfo, err := os.Lstat(filePath); err == nil { | ||||
| 						w.sendFileCreatedEventIfNew(filePath, fileInfo) | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			// Move to next event | ||||
| 			kevents = kevents[1:] | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// cleanup | ||||
| 	err := unix.Close(w.kq) | ||||
| 	if err != nil { | ||||
| 		// only way the previous loop breaks is if w.done was closed so we need to async send to w.Errors. | ||||
| 		select { | ||||
| 		case w.Errors <- err: | ||||
| 		default: | ||||
| 		} | ||||
| 	} | ||||
| 	close(w.Events) | ||||
| 	close(w.Errors) | ||||
| } | ||||
|  | ||||
| // newEvent returns an platform-independent Event based on kqueue Fflags. | ||||
| func newEvent(name string, mask uint32) Event { | ||||
| 	e := Event{Name: name} | ||||
| 	if mask&unix.NOTE_DELETE == unix.NOTE_DELETE { | ||||
| 		e.Op |= Remove | ||||
| 	} | ||||
| 	if mask&unix.NOTE_WRITE == unix.NOTE_WRITE { | ||||
| 		e.Op |= Write | ||||
| 	} | ||||
| 	if mask&unix.NOTE_RENAME == unix.NOTE_RENAME { | ||||
| 		e.Op |= Rename | ||||
| 	} | ||||
| 	if mask&unix.NOTE_ATTRIB == unix.NOTE_ATTRIB { | ||||
| 		e.Op |= Chmod | ||||
| 	} | ||||
| 	return e | ||||
| } | ||||
|  | ||||
| func newCreateEvent(name string) Event { | ||||
| 	return Event{Name: name, Op: Create} | ||||
| } | ||||
|  | ||||
| // watchDirectoryFiles to mimic inotify when adding a watch on a directory | ||||
| func (w *Watcher) watchDirectoryFiles(dirPath string) error { | ||||
| 	// Get all files | ||||
| 	files, err := ioutil.ReadDir(dirPath) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	for _, fileInfo := range files { | ||||
| 		filePath := filepath.Join(dirPath, fileInfo.Name()) | ||||
| 		filePath, err = w.internalWatch(filePath, fileInfo) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		w.mu.Lock() | ||||
| 		w.fileExists[filePath] = true | ||||
| 		w.mu.Unlock() | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // sendDirectoryEvents searches the directory for newly created files | ||||
| // and sends them over the event channel. This functionality is to have | ||||
| // the BSD version of fsnotify match Linux inotify which provides a | ||||
| // create event for files created in a watched directory. | ||||
| func (w *Watcher) sendDirectoryChangeEvents(dirPath string) { | ||||
| 	// Get all files | ||||
| 	files, err := ioutil.ReadDir(dirPath) | ||||
| 	if err != nil { | ||||
| 		select { | ||||
| 		case w.Errors <- err: | ||||
| 		case <-w.done: | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Search for new files | ||||
| 	for _, fileInfo := range files { | ||||
| 		filePath := filepath.Join(dirPath, fileInfo.Name()) | ||||
| 		err := w.sendFileCreatedEventIfNew(filePath, fileInfo) | ||||
|  | ||||
| 		if err != nil { | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // sendFileCreatedEvent sends a create event if the file isn't already being tracked. | ||||
| func (w *Watcher) sendFileCreatedEventIfNew(filePath string, fileInfo os.FileInfo) (err error) { | ||||
| 	w.mu.Lock() | ||||
| 	_, doesExist := w.fileExists[filePath] | ||||
| 	w.mu.Unlock() | ||||
| 	if !doesExist { | ||||
| 		// Send create event | ||||
| 		select { | ||||
| 		case w.Events <- newCreateEvent(filePath): | ||||
| 		case <-w.done: | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// like watchDirectoryFiles (but without doing another ReadDir) | ||||
| 	filePath, err = w.internalWatch(filePath, fileInfo) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	w.mu.Lock() | ||||
| 	w.fileExists[filePath] = true | ||||
| 	w.mu.Unlock() | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (w *Watcher) internalWatch(name string, fileInfo os.FileInfo) (string, error) { | ||||
| 	if fileInfo.IsDir() { | ||||
| 		// mimic Linux providing delete events for subdirectories | ||||
| 		// but preserve the flags used if currently watching subdirectory | ||||
| 		w.mu.Lock() | ||||
| 		flags := w.dirFlags[name] | ||||
| 		w.mu.Unlock() | ||||
|  | ||||
| 		flags |= unix.NOTE_DELETE | unix.NOTE_RENAME | ||||
| 		return w.addWatch(name, flags) | ||||
| 	} | ||||
|  | ||||
| 	// watch file to mimic Linux inotify | ||||
| 	return w.addWatch(name, noteAllEvents) | ||||
| } | ||||
|  | ||||
| // kqueue creates a new kernel event queue and returns a descriptor. | ||||
| func kqueue() (kq int, err error) { | ||||
| 	kq, err = unix.Kqueue() | ||||
| 	if kq == -1 { | ||||
| 		return kq, err | ||||
| 	} | ||||
| 	return kq, nil | ||||
| } | ||||
|  | ||||
| // register events with the queue | ||||
| func register(kq int, fds []int, flags int, fflags uint32) error { | ||||
| 	changes := make([]unix.Kevent_t, len(fds)) | ||||
|  | ||||
| 	for i, fd := range fds { | ||||
| 		// SetKevent converts int to the platform-specific types: | ||||
| 		unix.SetKevent(&changes[i], fd, unix.EVFILT_VNODE, flags) | ||||
| 		changes[i].Fflags = fflags | ||||
| 	} | ||||
|  | ||||
| 	// register the events | ||||
| 	success, err := unix.Kevent(kq, changes, nil, nil) | ||||
| 	if success == -1 { | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // read retrieves pending events, or waits until an event occurs. | ||||
| // A timeout of nil blocks indefinitely, while 0 polls the queue. | ||||
| func read(kq int, events []unix.Kevent_t, timeout *unix.Timespec) ([]unix.Kevent_t, error) { | ||||
| 	n, err := unix.Kevent(kq, nil, events, timeout) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return events[0:n], nil | ||||
| } | ||||
|  | ||||
| // durationToTimespec prepares a timeout value | ||||
| func durationToTimespec(d time.Duration) unix.Timespec { | ||||
| 	return unix.NsecToTimespec(d.Nanoseconds()) | ||||
| } | ||||
							
								
								
									
										11
									
								
								vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,11 +0,0 @@ | ||||
| // Copyright 2013 The Go Authors. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| // +build freebsd openbsd netbsd dragonfly | ||||
|  | ||||
| package fsnotify | ||||
|  | ||||
| import "golang.org/x/sys/unix" | ||||
|  | ||||
| const openMode = unix.O_NONBLOCK | unix.O_RDONLY | ||||
							
								
								
									
										12
									
								
								vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,12 +0,0 @@ | ||||
| // Copyright 2013 The Go Authors. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| // +build darwin | ||||
|  | ||||
| package fsnotify | ||||
|  | ||||
| import "golang.org/x/sys/unix" | ||||
|  | ||||
| // note: this constant is not defined on BSD | ||||
| const openMode = unix.O_EVTONLY | ||||
							
								
								
									
										561
									
								
								vendor/github.com/fsnotify/fsnotify/windows.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										561
									
								
								vendor/github.com/fsnotify/fsnotify/windows.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,561 +0,0 @@ | ||||
| // Copyright 2011 The Go Authors. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| // +build windows | ||||
|  | ||||
| package fsnotify | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"runtime" | ||||
| 	"sync" | ||||
| 	"syscall" | ||||
| 	"unsafe" | ||||
| ) | ||||
|  | ||||
| // Watcher watches a set of files, delivering events to a channel. | ||||
| type Watcher struct { | ||||
| 	Events   chan Event | ||||
| 	Errors   chan error | ||||
| 	isClosed bool           // Set to true when Close() is first called | ||||
| 	mu       sync.Mutex     // Map access | ||||
| 	port     syscall.Handle // Handle to completion port | ||||
| 	watches  watchMap       // Map of watches (key: i-number) | ||||
| 	input    chan *input    // Inputs to the reader are sent on this channel | ||||
| 	quit     chan chan<- error | ||||
| } | ||||
|  | ||||
| // NewWatcher establishes a new watcher with the underlying OS and begins waiting for events. | ||||
| func NewWatcher() (*Watcher, error) { | ||||
| 	port, e := syscall.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 0) | ||||
| 	if e != nil { | ||||
| 		return nil, os.NewSyscallError("CreateIoCompletionPort", e) | ||||
| 	} | ||||
| 	w := &Watcher{ | ||||
| 		port:    port, | ||||
| 		watches: make(watchMap), | ||||
| 		input:   make(chan *input, 1), | ||||
| 		Events:  make(chan Event, 50), | ||||
| 		Errors:  make(chan error), | ||||
| 		quit:    make(chan chan<- error, 1), | ||||
| 	} | ||||
| 	go w.readEvents() | ||||
| 	return w, nil | ||||
| } | ||||
|  | ||||
| // Close removes all watches and closes the events channel. | ||||
| func (w *Watcher) Close() error { | ||||
| 	if w.isClosed { | ||||
| 		return nil | ||||
| 	} | ||||
| 	w.isClosed = true | ||||
|  | ||||
| 	// Send "quit" message to the reader goroutine | ||||
| 	ch := make(chan error) | ||||
| 	w.quit <- ch | ||||
| 	if err := w.wakeupReader(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return <-ch | ||||
| } | ||||
|  | ||||
| // Add starts watching the named file or directory (non-recursively). | ||||
| func (w *Watcher) Add(name string) error { | ||||
| 	if w.isClosed { | ||||
| 		return errors.New("watcher already closed") | ||||
| 	} | ||||
| 	in := &input{ | ||||
| 		op:    opAddWatch, | ||||
| 		path:  filepath.Clean(name), | ||||
| 		flags: sysFSALLEVENTS, | ||||
| 		reply: make(chan error), | ||||
| 	} | ||||
| 	w.input <- in | ||||
| 	if err := w.wakeupReader(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return <-in.reply | ||||
| } | ||||
|  | ||||
| // Remove stops watching the the named file or directory (non-recursively). | ||||
| func (w *Watcher) Remove(name string) error { | ||||
| 	in := &input{ | ||||
| 		op:    opRemoveWatch, | ||||
| 		path:  filepath.Clean(name), | ||||
| 		reply: make(chan error), | ||||
| 	} | ||||
| 	w.input <- in | ||||
| 	if err := w.wakeupReader(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return <-in.reply | ||||
| } | ||||
|  | ||||
| const ( | ||||
| 	// Options for AddWatch | ||||
| 	sysFSONESHOT = 0x80000000 | ||||
| 	sysFSONLYDIR = 0x1000000 | ||||
|  | ||||
| 	// Events | ||||
| 	sysFSACCESS     = 0x1 | ||||
| 	sysFSALLEVENTS  = 0xfff | ||||
| 	sysFSATTRIB     = 0x4 | ||||
| 	sysFSCLOSE      = 0x18 | ||||
| 	sysFSCREATE     = 0x100 | ||||
| 	sysFSDELETE     = 0x200 | ||||
| 	sysFSDELETESELF = 0x400 | ||||
| 	sysFSMODIFY     = 0x2 | ||||
| 	sysFSMOVE       = 0xc0 | ||||
| 	sysFSMOVEDFROM  = 0x40 | ||||
| 	sysFSMOVEDTO    = 0x80 | ||||
| 	sysFSMOVESELF   = 0x800 | ||||
|  | ||||
| 	// Special events | ||||
| 	sysFSIGNORED   = 0x8000 | ||||
| 	sysFSQOVERFLOW = 0x4000 | ||||
| ) | ||||
|  | ||||
| func newEvent(name string, mask uint32) Event { | ||||
| 	e := Event{Name: name} | ||||
| 	if mask&sysFSCREATE == sysFSCREATE || mask&sysFSMOVEDTO == sysFSMOVEDTO { | ||||
| 		e.Op |= Create | ||||
| 	} | ||||
| 	if mask&sysFSDELETE == sysFSDELETE || mask&sysFSDELETESELF == sysFSDELETESELF { | ||||
| 		e.Op |= Remove | ||||
| 	} | ||||
| 	if mask&sysFSMODIFY == sysFSMODIFY { | ||||
| 		e.Op |= Write | ||||
| 	} | ||||
| 	if mask&sysFSMOVE == sysFSMOVE || mask&sysFSMOVESELF == sysFSMOVESELF || mask&sysFSMOVEDFROM == sysFSMOVEDFROM { | ||||
| 		e.Op |= Rename | ||||
| 	} | ||||
| 	if mask&sysFSATTRIB == sysFSATTRIB { | ||||
| 		e.Op |= Chmod | ||||
| 	} | ||||
| 	return e | ||||
| } | ||||
|  | ||||
| const ( | ||||
| 	opAddWatch = iota | ||||
| 	opRemoveWatch | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	provisional uint64 = 1 << (32 + iota) | ||||
| ) | ||||
|  | ||||
| type input struct { | ||||
| 	op    int | ||||
| 	path  string | ||||
| 	flags uint32 | ||||
| 	reply chan error | ||||
| } | ||||
|  | ||||
| type inode struct { | ||||
| 	handle syscall.Handle | ||||
| 	volume uint32 | ||||
| 	index  uint64 | ||||
| } | ||||
|  | ||||
| type watch struct { | ||||
| 	ov     syscall.Overlapped | ||||
| 	ino    *inode            // i-number | ||||
| 	path   string            // Directory path | ||||
| 	mask   uint64            // Directory itself is being watched with these notify flags | ||||
| 	names  map[string]uint64 // Map of names being watched and their notify flags | ||||
| 	rename string            // Remembers the old name while renaming a file | ||||
| 	buf    [4096]byte | ||||
| } | ||||
|  | ||||
| type indexMap map[uint64]*watch | ||||
| type watchMap map[uint32]indexMap | ||||
|  | ||||
| func (w *Watcher) wakeupReader() error { | ||||
| 	e := syscall.PostQueuedCompletionStatus(w.port, 0, 0, nil) | ||||
| 	if e != nil { | ||||
| 		return os.NewSyscallError("PostQueuedCompletionStatus", e) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func getDir(pathname string) (dir string, err error) { | ||||
| 	attr, e := syscall.GetFileAttributes(syscall.StringToUTF16Ptr(pathname)) | ||||
| 	if e != nil { | ||||
| 		return "", os.NewSyscallError("GetFileAttributes", e) | ||||
| 	} | ||||
| 	if attr&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 { | ||||
| 		dir = pathname | ||||
| 	} else { | ||||
| 		dir, _ = filepath.Split(pathname) | ||||
| 		dir = filepath.Clean(dir) | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func getIno(path string) (ino *inode, err error) { | ||||
| 	h, e := syscall.CreateFile(syscall.StringToUTF16Ptr(path), | ||||
| 		syscall.FILE_LIST_DIRECTORY, | ||||
| 		syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, | ||||
| 		nil, syscall.OPEN_EXISTING, | ||||
| 		syscall.FILE_FLAG_BACKUP_SEMANTICS|syscall.FILE_FLAG_OVERLAPPED, 0) | ||||
| 	if e != nil { | ||||
| 		return nil, os.NewSyscallError("CreateFile", e) | ||||
| 	} | ||||
| 	var fi syscall.ByHandleFileInformation | ||||
| 	if e = syscall.GetFileInformationByHandle(h, &fi); e != nil { | ||||
| 		syscall.CloseHandle(h) | ||||
| 		return nil, os.NewSyscallError("GetFileInformationByHandle", e) | ||||
| 	} | ||||
| 	ino = &inode{ | ||||
| 		handle: h, | ||||
| 		volume: fi.VolumeSerialNumber, | ||||
| 		index:  uint64(fi.FileIndexHigh)<<32 | uint64(fi.FileIndexLow), | ||||
| 	} | ||||
| 	return ino, nil | ||||
| } | ||||
|  | ||||
| // Must run within the I/O thread. | ||||
| func (m watchMap) get(ino *inode) *watch { | ||||
| 	if i := m[ino.volume]; i != nil { | ||||
| 		return i[ino.index] | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Must run within the I/O thread. | ||||
| func (m watchMap) set(ino *inode, watch *watch) { | ||||
| 	i := m[ino.volume] | ||||
| 	if i == nil { | ||||
| 		i = make(indexMap) | ||||
| 		m[ino.volume] = i | ||||
| 	} | ||||
| 	i[ino.index] = watch | ||||
| } | ||||
|  | ||||
| // Must run within the I/O thread. | ||||
| func (w *Watcher) addWatch(pathname string, flags uint64) error { | ||||
| 	dir, err := getDir(pathname) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if flags&sysFSONLYDIR != 0 && pathname != dir { | ||||
| 		return nil | ||||
| 	} | ||||
| 	ino, err := getIno(dir) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	w.mu.Lock() | ||||
| 	watchEntry := w.watches.get(ino) | ||||
| 	w.mu.Unlock() | ||||
| 	if watchEntry == nil { | ||||
| 		if _, e := syscall.CreateIoCompletionPort(ino.handle, w.port, 0, 0); e != nil { | ||||
| 			syscall.CloseHandle(ino.handle) | ||||
| 			return os.NewSyscallError("CreateIoCompletionPort", e) | ||||
| 		} | ||||
| 		watchEntry = &watch{ | ||||
| 			ino:   ino, | ||||
| 			path:  dir, | ||||
| 			names: make(map[string]uint64), | ||||
| 		} | ||||
| 		w.mu.Lock() | ||||
| 		w.watches.set(ino, watchEntry) | ||||
| 		w.mu.Unlock() | ||||
| 		flags |= provisional | ||||
| 	} else { | ||||
| 		syscall.CloseHandle(ino.handle) | ||||
| 	} | ||||
| 	if pathname == dir { | ||||
| 		watchEntry.mask |= flags | ||||
| 	} else { | ||||
| 		watchEntry.names[filepath.Base(pathname)] |= flags | ||||
| 	} | ||||
| 	if err = w.startRead(watchEntry); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if pathname == dir { | ||||
| 		watchEntry.mask &= ^provisional | ||||
| 	} else { | ||||
| 		watchEntry.names[filepath.Base(pathname)] &= ^provisional | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Must run within the I/O thread. | ||||
| func (w *Watcher) remWatch(pathname string) error { | ||||
| 	dir, err := getDir(pathname) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	ino, err := getIno(dir) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	w.mu.Lock() | ||||
| 	watch := w.watches.get(ino) | ||||
| 	w.mu.Unlock() | ||||
| 	if watch == nil { | ||||
| 		return fmt.Errorf("can't remove non-existent watch for: %s", pathname) | ||||
| 	} | ||||
| 	if pathname == dir { | ||||
| 		w.sendEvent(watch.path, watch.mask&sysFSIGNORED) | ||||
| 		watch.mask = 0 | ||||
| 	} else { | ||||
| 		name := filepath.Base(pathname) | ||||
| 		w.sendEvent(filepath.Join(watch.path, name), watch.names[name]&sysFSIGNORED) | ||||
| 		delete(watch.names, name) | ||||
| 	} | ||||
| 	return w.startRead(watch) | ||||
| } | ||||
|  | ||||
| // Must run within the I/O thread. | ||||
| func (w *Watcher) deleteWatch(watch *watch) { | ||||
| 	for name, mask := range watch.names { | ||||
| 		if mask&provisional == 0 { | ||||
| 			w.sendEvent(filepath.Join(watch.path, name), mask&sysFSIGNORED) | ||||
| 		} | ||||
| 		delete(watch.names, name) | ||||
| 	} | ||||
| 	if watch.mask != 0 { | ||||
| 		if watch.mask&provisional == 0 { | ||||
| 			w.sendEvent(watch.path, watch.mask&sysFSIGNORED) | ||||
| 		} | ||||
| 		watch.mask = 0 | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Must run within the I/O thread. | ||||
| func (w *Watcher) startRead(watch *watch) error { | ||||
| 	if e := syscall.CancelIo(watch.ino.handle); e != nil { | ||||
| 		w.Errors <- os.NewSyscallError("CancelIo", e) | ||||
| 		w.deleteWatch(watch) | ||||
| 	} | ||||
| 	mask := toWindowsFlags(watch.mask) | ||||
| 	for _, m := range watch.names { | ||||
| 		mask |= toWindowsFlags(m) | ||||
| 	} | ||||
| 	if mask == 0 { | ||||
| 		if e := syscall.CloseHandle(watch.ino.handle); e != nil { | ||||
| 			w.Errors <- os.NewSyscallError("CloseHandle", e) | ||||
| 		} | ||||
| 		w.mu.Lock() | ||||
| 		delete(w.watches[watch.ino.volume], watch.ino.index) | ||||
| 		w.mu.Unlock() | ||||
| 		return nil | ||||
| 	} | ||||
| 	e := syscall.ReadDirectoryChanges(watch.ino.handle, &watch.buf[0], | ||||
| 		uint32(unsafe.Sizeof(watch.buf)), false, mask, nil, &watch.ov, 0) | ||||
| 	if e != nil { | ||||
| 		err := os.NewSyscallError("ReadDirectoryChanges", e) | ||||
| 		if e == syscall.ERROR_ACCESS_DENIED && watch.mask&provisional == 0 { | ||||
| 			// Watched directory was probably removed | ||||
| 			if w.sendEvent(watch.path, watch.mask&sysFSDELETESELF) { | ||||
| 				if watch.mask&sysFSONESHOT != 0 { | ||||
| 					watch.mask = 0 | ||||
| 				} | ||||
| 			} | ||||
| 			err = nil | ||||
| 		} | ||||
| 		w.deleteWatch(watch) | ||||
| 		w.startRead(watch) | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // readEvents reads from the I/O completion port, converts the | ||||
| // received events into Event objects and sends them via the Events channel. | ||||
| // Entry point to the I/O thread. | ||||
| func (w *Watcher) readEvents() { | ||||
| 	var ( | ||||
| 		n, key uint32 | ||||
| 		ov     *syscall.Overlapped | ||||
| 	) | ||||
| 	runtime.LockOSThread() | ||||
|  | ||||
| 	for { | ||||
| 		e := syscall.GetQueuedCompletionStatus(w.port, &n, &key, &ov, syscall.INFINITE) | ||||
| 		watch := (*watch)(unsafe.Pointer(ov)) | ||||
|  | ||||
| 		if watch == nil { | ||||
| 			select { | ||||
| 			case ch := <-w.quit: | ||||
| 				w.mu.Lock() | ||||
| 				var indexes []indexMap | ||||
| 				for _, index := range w.watches { | ||||
| 					indexes = append(indexes, index) | ||||
| 				} | ||||
| 				w.mu.Unlock() | ||||
| 				for _, index := range indexes { | ||||
| 					for _, watch := range index { | ||||
| 						w.deleteWatch(watch) | ||||
| 						w.startRead(watch) | ||||
| 					} | ||||
| 				} | ||||
| 				var err error | ||||
| 				if e := syscall.CloseHandle(w.port); e != nil { | ||||
| 					err = os.NewSyscallError("CloseHandle", e) | ||||
| 				} | ||||
| 				close(w.Events) | ||||
| 				close(w.Errors) | ||||
| 				ch <- err | ||||
| 				return | ||||
| 			case in := <-w.input: | ||||
| 				switch in.op { | ||||
| 				case opAddWatch: | ||||
| 					in.reply <- w.addWatch(in.path, uint64(in.flags)) | ||||
| 				case opRemoveWatch: | ||||
| 					in.reply <- w.remWatch(in.path) | ||||
| 				} | ||||
| 			default: | ||||
| 			} | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		switch e { | ||||
| 		case syscall.ERROR_MORE_DATA: | ||||
| 			if watch == nil { | ||||
| 				w.Errors <- errors.New("ERROR_MORE_DATA has unexpectedly null lpOverlapped buffer") | ||||
| 			} else { | ||||
| 				// The i/o succeeded but the buffer is full. | ||||
| 				// In theory we should be building up a full packet. | ||||
| 				// In practice we can get away with just carrying on. | ||||
| 				n = uint32(unsafe.Sizeof(watch.buf)) | ||||
| 			} | ||||
| 		case syscall.ERROR_ACCESS_DENIED: | ||||
| 			// Watched directory was probably removed | ||||
| 			w.sendEvent(watch.path, watch.mask&sysFSDELETESELF) | ||||
| 			w.deleteWatch(watch) | ||||
| 			w.startRead(watch) | ||||
| 			continue | ||||
| 		case syscall.ERROR_OPERATION_ABORTED: | ||||
| 			// CancelIo was called on this handle | ||||
| 			continue | ||||
| 		default: | ||||
| 			w.Errors <- os.NewSyscallError("GetQueuedCompletionPort", e) | ||||
| 			continue | ||||
| 		case nil: | ||||
| 		} | ||||
|  | ||||
| 		var offset uint32 | ||||
| 		for { | ||||
| 			if n == 0 { | ||||
| 				w.Events <- newEvent("", sysFSQOVERFLOW) | ||||
| 				w.Errors <- errors.New("short read in readEvents()") | ||||
| 				break | ||||
| 			} | ||||
|  | ||||
| 			// Point "raw" to the event in the buffer | ||||
| 			raw := (*syscall.FileNotifyInformation)(unsafe.Pointer(&watch.buf[offset])) | ||||
| 			buf := (*[syscall.MAX_PATH]uint16)(unsafe.Pointer(&raw.FileName)) | ||||
| 			name := syscall.UTF16ToString(buf[:raw.FileNameLength/2]) | ||||
| 			fullname := filepath.Join(watch.path, name) | ||||
|  | ||||
| 			var mask uint64 | ||||
| 			switch raw.Action { | ||||
| 			case syscall.FILE_ACTION_REMOVED: | ||||
| 				mask = sysFSDELETESELF | ||||
| 			case syscall.FILE_ACTION_MODIFIED: | ||||
| 				mask = sysFSMODIFY | ||||
| 			case syscall.FILE_ACTION_RENAMED_OLD_NAME: | ||||
| 				watch.rename = name | ||||
| 			case syscall.FILE_ACTION_RENAMED_NEW_NAME: | ||||
| 				if watch.names[watch.rename] != 0 { | ||||
| 					watch.names[name] |= watch.names[watch.rename] | ||||
| 					delete(watch.names, watch.rename) | ||||
| 					mask = sysFSMOVESELF | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			sendNameEvent := func() { | ||||
| 				if w.sendEvent(fullname, watch.names[name]&mask) { | ||||
| 					if watch.names[name]&sysFSONESHOT != 0 { | ||||
| 						delete(watch.names, name) | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			if raw.Action != syscall.FILE_ACTION_RENAMED_NEW_NAME { | ||||
| 				sendNameEvent() | ||||
| 			} | ||||
| 			if raw.Action == syscall.FILE_ACTION_REMOVED { | ||||
| 				w.sendEvent(fullname, watch.names[name]&sysFSIGNORED) | ||||
| 				delete(watch.names, name) | ||||
| 			} | ||||
| 			if w.sendEvent(fullname, watch.mask&toFSnotifyFlags(raw.Action)) { | ||||
| 				if watch.mask&sysFSONESHOT != 0 { | ||||
| 					watch.mask = 0 | ||||
| 				} | ||||
| 			} | ||||
| 			if raw.Action == syscall.FILE_ACTION_RENAMED_NEW_NAME { | ||||
| 				fullname = filepath.Join(watch.path, watch.rename) | ||||
| 				sendNameEvent() | ||||
| 			} | ||||
|  | ||||
| 			// Move to the next event in the buffer | ||||
| 			if raw.NextEntryOffset == 0 { | ||||
| 				break | ||||
| 			} | ||||
| 			offset += raw.NextEntryOffset | ||||
|  | ||||
| 			// Error! | ||||
| 			if offset >= n { | ||||
| 				w.Errors <- errors.New("Windows system assumed buffer larger than it is, events have likely been missed.") | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if err := w.startRead(watch); err != nil { | ||||
| 			w.Errors <- err | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (w *Watcher) sendEvent(name string, mask uint64) bool { | ||||
| 	if mask == 0 { | ||||
| 		return false | ||||
| 	} | ||||
| 	event := newEvent(name, uint32(mask)) | ||||
| 	select { | ||||
| 	case ch := <-w.quit: | ||||
| 		w.quit <- ch | ||||
| 	case w.Events <- event: | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func toWindowsFlags(mask uint64) uint32 { | ||||
| 	var m uint32 | ||||
| 	if mask&sysFSACCESS != 0 { | ||||
| 		m |= syscall.FILE_NOTIFY_CHANGE_LAST_ACCESS | ||||
| 	} | ||||
| 	if mask&sysFSMODIFY != 0 { | ||||
| 		m |= syscall.FILE_NOTIFY_CHANGE_LAST_WRITE | ||||
| 	} | ||||
| 	if mask&sysFSATTRIB != 0 { | ||||
| 		m |= syscall.FILE_NOTIFY_CHANGE_ATTRIBUTES | ||||
| 	} | ||||
| 	if mask&(sysFSMOVE|sysFSCREATE|sysFSDELETE) != 0 { | ||||
| 		m |= syscall.FILE_NOTIFY_CHANGE_FILE_NAME | syscall.FILE_NOTIFY_CHANGE_DIR_NAME | ||||
| 	} | ||||
| 	return m | ||||
| } | ||||
|  | ||||
| func toFSnotifyFlags(action uint32) uint64 { | ||||
| 	switch action { | ||||
| 	case syscall.FILE_ACTION_ADDED: | ||||
| 		return sysFSCREATE | ||||
| 	case syscall.FILE_ACTION_REMOVED: | ||||
| 		return sysFSDELETE | ||||
| 	case syscall.FILE_ACTION_MODIFIED: | ||||
| 		return sysFSMODIFY | ||||
| 	case syscall.FILE_ACTION_RENAMED_OLD_NAME: | ||||
| 		return sysFSMOVEDFROM | ||||
| 	case syscall.FILE_ACTION_RENAMED_NEW_NAME: | ||||
| 		return sysFSMOVEDTO | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
							
								
								
									
										3
									
								
								vendor/modules.txt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								vendor/modules.txt
									
									
									
									
										vendored
									
									
								
							| @@ -30,9 +30,6 @@ github.com/fatih/color | ||||
| # github.com/fsmiamoto/git-todo-parser v0.0.5 | ||||
| ## explicit; go 1.13 | ||||
| github.com/fsmiamoto/git-todo-parser/todo | ||||
| # github.com/fsnotify/fsnotify v1.4.7 | ||||
| ## explicit | ||||
| github.com/fsnotify/fsnotify | ||||
| # github.com/gdamore/encoding v1.0.0 | ||||
| ## explicit; go 1.9 | ||||
| github.com/gdamore/encoding | ||||
|   | ||||
		Reference in New Issue
	
	Block a user