diff options
author | Andreas Rumpf <rumpf_a@web.de> | 2017-03-29 10:14:03 +0200 |
---|---|---|
committer | Andreas Rumpf <rumpf_a@web.de> | 2017-03-29 10:14:03 +0200 |
commit | 3ffde39cf416579144d37021f24c99e49efc0aee (patch) | |
tree | 329f976acf0c2b07da57e242820c8aba41c6f5d6 /lib/packages | |
parent | a88a9095654ec4c7f0dd803e164047a464fba829 (diff) | |
download | Nim-3ffde39cf416579144d37021f24c99e49efc0aee.tar.gz |
attempt to make travis OSX tests green and mandatory
Diffstat (limited to 'lib/packages')
-rw-r--r-- | lib/packages/fsmonitor.nim | 229 |
1 files changed, 229 insertions, 0 deletions
diff --git a/lib/packages/fsmonitor.nim b/lib/packages/fsmonitor.nim new file mode 100644 index 000000000..b22e84f44 --- /dev/null +++ b/lib/packages/fsmonitor.nim @@ -0,0 +1,229 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2012 Dominik Picheta +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module allows you to monitor files or directories for changes using +## asyncio. +## +## **Warning**: This module will likely disappear soon and be moved into a +## new Nimble package. +## +## Windows support is not yet implemented. +## +## **Note:** This module uses ``inotify`` on Linux (Other Unixes are not yet +## supported). ``inotify`` was merged into the 2.6.13 Linux kernel, this +## module will therefore not work with any Linux kernel prior to that, unless +## it has been patched to support inotify. + +when defined(linux) or defined(nimdoc): + from posix import read +else: + {.error: "Your platform is not supported.".} + +import inotify, os, asyncio, tables + +type + FSMonitor* = ref FSMonitorObj + FSMonitorObj = object of RootObj + fd: cint + handleEvent: proc (m: FSMonitor, ev: MonitorEvent) {.closure.} + targets: Table[cint, string] + + MonitorEventType* = enum ## Monitor event type + MonitorAccess, ## File was accessed. + MonitorAttrib, ## Metadata changed. + MonitorCloseWrite, ## Writable file was closed. + MonitorCloseNoWrite, ## Non-writable file closed. + MonitorCreate, ## Subfile was created. + MonitorDelete, ## Subfile was deleted. + MonitorDeleteSelf, ## Watched file/directory was itself deleted. + MonitorModify, ## File was modified. + MonitorMoveSelf, ## Self was moved. + MonitorMoved, ## File was moved. + MonitorOpen, ## File was opened. + MonitorAll ## Filter for all event types. + + MonitorEvent* = object + case kind*: MonitorEventType ## Type of the event. + of MonitorMoveSelf, MonitorMoved: + oldPath*: string ## Old absolute location + newPath*: string ## New absolute location + else: + fullname*: string ## Absolute filename of the file/directory affected. + name*: string ## Non absolute filepath of the file/directory + ## affected relative to the directory watched. + ## "" if this event refers to the file/directory + ## watched. + wd*: cint ## Watch descriptor. + +{.deprecated: [PFSMonitor: FSMonitor, TFSMonitor: FSMonitorObj, + TMonitorEventType: MonitorEventType, TMonitorEvent: MonitorEvent].} + +const + MaxEvents = 100 + +proc newMonitor*(): FSMonitor = + ## Creates a new file system monitor. + new(result) + result.targets = initTable[cint, string]() + result.fd = inotifyInit() + if result.fd < 0: + raiseOSError(osLastError()) + +proc add*(monitor: FSMonitor, target: string, + filters = {MonitorAll}): cint {.discardable.} = + ## Adds ``target`` which may be a directory or a file to the list of + ## watched paths of ``monitor``. + ## You can specify the events to report using the ``filters`` parameter. + + var INFilter = 0 + for f in filters: + case f + of MonitorAccess: INFilter = INFilter or IN_ACCESS + of MonitorAttrib: INFilter = INFilter or IN_ATTRIB + of MonitorCloseWrite: INFilter = INFilter or IN_CLOSE_WRITE + of MonitorCloseNoWrite: INFilter = INFilter or IN_CLOSE_NO_WRITE + of MonitorCreate: INFilter = INFilter or IN_CREATE + of MonitorDelete: INFilter = INFilter or IN_DELETE + of MonitorDeleteSelf: INFilter = INFilter or IN_DELETE_SELF + of MonitorModify: INFilter = INFilter or IN_MODIFY + of MonitorMoveSelf: INFilter = INFilter or IN_MOVE_SELF + of MonitorMoved: INFilter = INFilter or IN_MOVED_FROM or IN_MOVED_TO + of MonitorOpen: INFilter = INFilter or IN_OPEN + of MonitorAll: INFilter = INFilter or IN_ALL_EVENTS + + result = inotifyAddWatch(monitor.fd, target, INFilter.uint32) + if result < 0: + raiseOSError(osLastError()) + monitor.targets.add(result, target) + +proc del*(monitor: FSMonitor, wd: cint) = + ## Removes watched directory or file as specified by ``wd`` from ``monitor``. + ## + ## If ``wd`` is not a part of ``monitor`` an EOS error is raised. + if inotifyRmWatch(monitor.fd, wd) < 0: + raiseOSError(osLastError()) + +proc getEvent(m: FSMonitor, fd: cint): seq[MonitorEvent] = + result = @[] + let size = (sizeof(INotifyEvent)+2000)*MaxEvents + var buffer = newString(size) + + let le = read(fd, addr(buffer[0]), size) + + var movedFrom = initTable[cint, tuple[wd: cint, old: string]]() + + var i = 0 + while i < le: + var event = cast[ptr INotifyEvent](addr(buffer[i])) + var mev: MonitorEvent + mev.wd = event.wd + if event.len.int != 0: + let cstr = event.name.addr.cstring + mev.name = $cstr + else: + mev.name = "" + + if (event.mask.int and IN_MOVED_FROM) != 0: + # Moved from event, add to m's collection + movedFrom.add(event.cookie.cint, (mev.wd, mev.name)) + inc(i, sizeof(INotifyEvent) + event.len.int) + continue + elif (event.mask.int and IN_MOVED_TO) != 0: + mev.kind = MonitorMoved + assert movedFrom.hasKey(event.cookie.cint) + # Find the MovedFrom event. + mev.oldPath = movedFrom[event.cookie.cint].old + mev.newPath = "" # Set later + # Delete it from the Table + movedFrom.del(event.cookie.cint) + elif (event.mask.int and IN_ACCESS) != 0: mev.kind = MonitorAccess + elif (event.mask.int and IN_ATTRIB) != 0: mev.kind = MonitorAttrib + elif (event.mask.int and IN_CLOSE_WRITE) != 0: + mev.kind = MonitorCloseWrite + elif (event.mask.int and IN_CLOSE_NOWRITE) != 0: + mev.kind = MonitorCloseNoWrite + elif (event.mask.int and IN_CREATE) != 0: mev.kind = MonitorCreate + elif (event.mask.int and IN_DELETE) != 0: + mev.kind = MonitorDelete + elif (event.mask.int and IN_DELETE_SELF) != 0: + mev.kind = MonitorDeleteSelf + elif (event.mask.int and IN_MODIFY) != 0: mev.kind = MonitorModify + elif (event.mask.int and IN_MOVE_SELF) != 0: + mev.kind = MonitorMoveSelf + elif (event.mask.int and IN_OPEN) != 0: mev.kind = MonitorOpen + + if mev.kind != MonitorMoved: + mev.fullname = "" + + result.add(mev) + inc(i, sizeof(INotifyEvent) + event.len.int) + + # If movedFrom events have not been matched with a moveTo. File has + # been moved to an unwatched location, emit a MonitorDelete. + for cookie, t in pairs(movedFrom): + var mev: MonitorEvent + mev.kind = MonitorDelete + mev.wd = t.wd + mev.name = t.old + result.add(mev) + +proc FSMonitorRead(h: RootRef) = + var events = FSMonitor(h).getEvent(FSMonitor(h).fd) + #var newEv: MonitorEvent + for ev in events: + var target = FSMonitor(h).targets[ev.wd] + var newEv = ev + if newEv.kind == MonitorMoved: + newEv.oldPath = target / newEv.oldPath + newEv.newPath = target / newEv.name + else: + newEv.fullName = target / newEv.name + FSMonitor(h).handleEvent(FSMonitor(h), newEv) + +proc toDelegate(m: FSMonitor): Delegate = + result = newDelegate() + result.deleVal = m + result.fd = (type(result.fd))(m.fd) + result.mode = fmRead + result.handleRead = FSMonitorRead + result.open = true + +proc register*(d: Dispatcher, monitor: FSMonitor, + handleEvent: proc (m: FSMonitor, ev: MonitorEvent) {.closure.}) = + ## Registers ``monitor`` with dispatcher ``d``. + monitor.handleEvent = handleEvent + var deleg = toDelegate(monitor) + d.register(deleg) + +when not defined(testing) and isMainModule: + proc main = + var + disp = newDispatcher() + monitor = newMonitor() + n = 0 + n = monitor.add("/tmp") + assert n == 1 + n = monitor.add("/tmp", {MonitorAll}) + assert n == 1 + n = monitor.add("/tmp", {MonitorCloseWrite, MonitorCloseNoWrite}) + assert n == 1 + n = monitor.add("/tmp", {MonitorMoved, MonitorOpen, MonitorAccess}) + assert n == 1 + disp.register(monitor, + proc (m: FSMonitor, ev: MonitorEvent) = + echo("Got event: ", ev.kind) + if ev.kind == MonitorMoved: + echo("From ", ev.oldPath, " to ", ev.newPath) + echo("Name is ", ev.name) + else: + echo("Name ", ev.name, " fullname ", ev.fullName)) + + while true: + if not disp.poll(): break + main() |