hinotify 0.2 → 0.3
raw patch · 19 files changed
+43/−949 lines, 19 filesdep ~basePVP ok
version bump matches the API change (PVP)
Dependency ranges changed: base
API changes (from Hackage documentation)
+ System.INotify: killINotify :: INotify -> IO ()
+ System.INotify: withINotify :: (INotify -> IO a) -> IO a
Files
- ANNOUNCE +0/−58
- Makefile +0/−7
- README +5/−2
- README.html +0/−179
- examples/DirTree/DirTree.hs +0/−111
- examples/simple/simple.hs +0/−17
- hinotify.cabal +3/−3
- hinotify.css +0/−89
- include/inotify-syscalls.h +0/−61
- include/inotify.h +0/−113
- src/System/INotify.hsc +34/−18
- src/System/INotify/Masks.hsc +1/−33
- tests/Utils.hs +0/−55
- tests/test-all +0/−31
- tests/test001-list-dir-contents.hs +0/−20
- tests/test002-writefile.hs +0/−24
- tests/test003-removefile.hs +0/−35
- tests/test004-modify-file.hs +0/−47
- tests/test005-move-file.hs +0/−46
− ANNOUNCE
@@ -1,58 +0,0 @@-Greetings!--I'm pleased to announce hinotify 0.1, a library to inotify[1] which has-been part of the Linux kernel since 2.6.13.--inotify provides file system event notification, simply add a watcher to-a file or directory and get an event when it is accessed or modified.--The API basically consists of:--inotify_init :: IO INotify-inotify_add_watch :: INotify- -> [EventVariety] -- different events to listen on- -> FilePath -- file/directory to watch- -> (Event -> IO ()) -- event handler- -> IO WatchDescriptor-inotify_rm_watch :: INotify -> WatchDescriptor -> IO ()--A sample program:--> import System.Directory-> import System.IO->-> import System.INotify->-> main :: IO ()-> main = do-> inotify <- inotify_init-> print inotify-> home <- getHomeDirectory-> wd <- inotify_add_watch inotify-> [Open,Close,Access,Modify,Move]-> home-> print-> print wd-> putStrLn "Listens to your home directory. Hit enter to terminate."-> getLine-> inotify_rm_watch inotify wd--The code is available via www:--http://haskell.org/~kolmodin/code/hinotify/download/hinotify-0.1.tar.gz--and via darcs:-- darcs get http://haskell.org/~kolmodin/code/hinotify/--The API is available at:-- http://haskell.org/~kolmodin/code/hinotify/docs/api/--The library is very young and I'm most grateful for feedback on the API,-and what else you might have to suggest.--Cheers,- Lennart Kolmodin--[1] http://www.kernel.org/pub/linux/kernel/people/rml/inotify/
− Makefile
@@ -1,7 +0,0 @@--.PHONY : docs--docs : README.html--%.html : %- pandoc -s -S --toc -c hinotify.css $< -o $@
README view
@@ -1,6 +1,6 @@ % hinotify: inotify for Haskell % Lennart Kolmodin <kolmodin@gentoo.org>-% December 26, 2007+% December 26, 2007-2009 About -----@@ -11,7 +11,7 @@ inotify provides file system event notification, simply add a watcher to a file or directory and get an event when it is accessed or modified. -This module is named `inotify`.+This module is named `hinotify`. See example code in the `examples` directory, distributed with the source code.@@ -20,6 +20,9 @@ News ----++**hinotify 0.3**+: * Compiles with GHC 6.12, GHC 6.10.4, GHC 6.8.2 and GHC 6.6.1 **hinotify 0.2** : * Updates to the API
− README.html
@@ -1,179 +0,0 @@-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">-<html xmlns="http://www.w3.org/1999/xhtml"-><head- ><title- >hinotify: inotify for Haskell</title- ><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"- /><meta name="generator" content="pandoc"- /><meta name="author" content="Lennart Kolmodin <kolmodin@gentoo.org>"- /><meta name="date" content="December 26, 2007"- /><link rel="stylesheet" href="hinotify.css" type="text/css" media="all" />-</head- ><body- ><h1 class="title"- >hinotify: inotify for Haskell</h1- ><div id="toc"- ><ul- ><li- ><a href="#about" id="TOC-about"- >About</a- ></li- ><li- ><a href="#news" id="TOC-news"- >News</a- ></li- ><li- ><a href="#api" id="TOC-api"- >API</a- ></li- ><li- ><a href="#download" id="TOC-download"- >Download</a- ></li- ><li- ><a href="#author" id="TOC-author"- >Author</a- ></li- ><li- ><a href="#legal" id="TOC-legal"- >Legal</a- ></li- ></ul- ></div- ><h2 id="about"- ><a href="#TOC-about"- >About</a- ></h2- ><p- >hinotify, a library to <a href="http://www.kernel.org/pub/linux/kernel/people/rml/inotify/"- >inotify</a- > which has been part of the Linux kernel since 2.6.13.</p- ><p- >inotify provides file system event notification, simply add a watcher to a file or directory and get an event when it is accessed or modified.</p- ><p- >This module is named <code- >inotify</code- >.</p- ><p- >See example code in the <code- >examples</code- > directory, distributed with the source code.</p- ><h2 id="news"- ><a href="#TOC-news"- >News</a- ></h2- ><dl- ><dt- ><strong- >hinotify 0.2</strong- ></dt- ><dd- ><ul- ><li- >Updates to the API<ul- ><li- >Function names is now in semiCamelCase</li- ><li- >Restructure event parameters to make it more consistent</li- ></ul- ></li- ><li- >Small test suit in <code- >tests/</code- ></li- ><li- >Compiles with GHC 6.8.2 and GHC 6.6.1</li- ><li- >Requires Cabal 1.2</li- ></ul- ></dd- ><dt- ><strong- >hinotify 0.1</strong- ></dt- ><dd- >Initial release</dd- ></dl- ><h2 id="api"- ><a href="#TOC-api"- >API</a- ></h2- ><p- >The API basically consists of:</p- ><pre- ><code- >initINotify :: IO INotify-addWatch :: INotify- -> [EventVariety] -- different events to listen on- -> FilePath -- file/directory to watch- -> (Event -> IO ()) -- event handler- -> IO WatchDescriptor-removeWatch :: INotify -> WatchDescriptor -> IO ()-</code- ></pre- ><p- >A sample program:</p- ><pre- ><code- >import System.Directory-import System.IO--import System.INotify--main :: IO ()-main = do- inotify <- initINotify- print inotify- home <- getHomeDirectory- wd <- addWatch- inotify- [Open,Close,Access,Modify,Move]- home- print- print wd- putStrLn "Listens to your home directory. Hit enter to terminate."- getLine- removeWatch inotify wd-</code- ></pre- ><h2 id="download"- ><a href="#TOC-download"- >Download</a- ></h2- ><p- >The code is available via the <a href="http://haskell.org/~kolmodin/code/hinotify/"- >homepage</a- >, and via darcs:</p- ><pre- ><code- >darcs get --partial http://haskell.org/~kolmodin/code/hinotify/-</code- ></pre- ><p- >The <a href="http://haskell.org/~kolmodin/code/hinotify/docs/api/"- >API</a- > is available online.</p- ><p- >I’m most grateful for feedback on the API, and what else you might have to suggest.</p- ><h2 id="author"- ><a href="#TOC-author"- >Author</a- ></h2- ><p- >Lennart Kolmodin</p- ><p- ><code- >kolmodin at gentoo.org</code- ></p- ><h2 id="legal"- ><a href="#TOC-legal"- >Legal</a- ></h2- ><p- >This software is released under a BSD-style license. See LICENSE for more details.</p- ><p- >Copyright © 2007 Lennart Kolmodin</p- ></body- ></html->-
− examples/DirTree/DirTree.hs
@@ -1,111 +0,0 @@--- Duncan Coutts 2006-2007--- Requires gtk2hs 0.9.11--import qualified Data.Map as Map-import System.Directory-import System.Environment-import Control.Concurrent--import Data.IORef-import Control.Monad (liftM)-import Ix (inRange)--import System.INotify--import Graphics.UI.Gtk hiding (TreeModelFlags(TreeModelListOnly), cellText)-import Graphics.UI.Gtk.ModelView.CellLayout-import Graphics.UI.Gtk.ModelView.Types (TypedTreeModelClass)-import Graphics.UI.Gtk.ModelView.TreeModel (TreeModelFlags(TreeModelListOnly))-import Graphics.UI.Gtk.ModelView.CellRendererText (cellText)-import Graphics.UI.Gtk.ModelView.CustomStore-import Graphics.UI.Gtk.TreeList.TreeIter--instance TypedTreeModelClass (CustomTreeModel a)--dirModelNew :: FilePath -> IO (CustomTreeModel () FilePath)-dirModelNew path = do- - dirContents <- getDirectoryContents path- - rows <- newIORef (Map.fromList (zip dirContents (repeat ())))-- model <- customTreeModelNew () CustomTreeModelImplementation {- customTreeModelGetFlags = return [TreeModelListOnly],- customTreeModelGetIter = \[n] -> return (Just (TreeIter 0 (fromIntegral n) 0 0)),- customTreeModelGetPath = \(TreeIter _ n _ _) -> return [fromIntegral n],- customTreeModelGetRow = \(TreeIter _ n _ _) ->- readIORef rows >>= \rows -> - if inRange (0, Map.size rows - 1) (fromIntegral n)- then return (fst $ Map.elemAt (fromIntegral n) rows)- else fail "DirModel.getRow: iter does not refer to a valid entry",-- customTreeModelIterNext = \(TreeIter _ n _ _) ->- readIORef rows >>= \rows ->- if n >= fromIntegral (Map.size rows) - 1- then return Nothing- else return (Just (TreeIter 0 (n+1) 0 0)),- customTreeModelIterChildren = \_ -> return Nothing,- customTreeModelIterHasChild = \_ -> return False,- customTreeModelIterNChildren = \index -> readIORef rows >>= \rows ->- case index of- Nothing -> return $! Map.size rows- _ -> return 0,- customTreeModelIterNthChild = \index n -> case index of- Nothing -> return (Just (TreeIter 0 (fromIntegral n) 0 0))- _ -> return Nothing,- customTreeModelIterParent = \_ -> return Nothing,- customTreeModelRefNode = \_ -> return (),- customTreeModelUnrefNode = \_ -> return ()- }-- notify <- initINotify- watch <- addWatch notify [Move, Create, Delete] path $ \event -> - let add file = do - index <- atomicModifyIORef rows (\map ->- let map' = Map.insert file () map- in (map', Map.findIndex file map'))- treeModelRowInserted model [index] (TreeIter 0 (fromIntegral index) 0 0)- remove file = do- index <- atomicModifyIORef rows (\map ->- let map' = Map.delete file map- in (map', Map.findIndex file map))- treeModelRowDeleted model [index]-- in case event of- MovedIn _ file _ -> add file- MovedOut _ file _ -> remove file- Created _ file -> add file- Deleted _ file -> remove file- _ -> putStrLn $ "other event: " ++ show event-- -- TODO: on destroy model (INotify.removeWatch watch)- - return model--main = do- initGUI-- win <- windowNew- win `onDestroy` mainQuit-- args <- getArgs- let dir = case args of- [d] -> d- _ -> "."-- model <- dirModelNew dir-- tv <- treeViewNewWithModel model- win `containerAdd` tv-- tvc <- treeViewColumnNew- treeViewAppendColumn tv tvc-- text <- cellRendererTextNew- cellLayoutPackStart tvc text True- cellLayoutSetAttributes tvc text model- (\file -> [cellText := file])-- widgetShowAll win- timeoutAddFull (yield >> return True) priorityDefaultIdle 50- mainGUI
− examples/simple/simple.hs
@@ -1,17 +0,0 @@-module Main where--import System.Directory-import System.IO--import System.INotify--main :: IO ()-main = do- inotify <- initINotify- print inotify- home <- getHomeDirectory- wd <- addWatch inotify [Open,Close,Access,Modify,Move] home print- print wd- putStrLn "Listens to your home directory. Hit enter to terminate."- getLine- removeWatch inotify wd
hinotify.cabal view
@@ -1,5 +1,6 @@ name: hinotify-version: 0.2+version: 0.3+build-type: Simple synopsis: Haskell binding to INotify description: .@@ -22,7 +23,7 @@ library build-depends: unix if flag(split-base)- build-depends: base >= 3, containers, directory+ build-depends: base >= 3 && < 5, containers, directory else build-depends: base < 3 extensions: ForeignFunctionInterface@@ -34,5 +35,4 @@ ghc-options: -fvia-C -Wall - include-dirs: include hs-source-dirs: src
− hinotify.css
@@ -1,89 +0,0 @@-body {- margin: auto;- padding-right: 1em;- padding-left: 1em;- max-width: 50em; - border-left: 1px solid black;- border-right: 1px solid black;- color: black;- font-family: Verdana, sans-serif;- font-size: 100%;- line-height: 140%;- color: #333; -}-pre {- border: 1px dotted gray;- background-color: #9999ff;- color: #111111;- padding: 0.5em;-}-code {- font-family: monospace;- font-size: 110%;-}-h1 a, h2 a, h3 a, h4 a, h5 a { - text-decoration: none;- color: #000099; -}-h1, h2, h3, h4, h5 { font-family: verdana;- font-weight: bold;- border-bottom: 1px dotted black;- color: #000099; }-h1 {- font-size: 130%;-}--h2 {- font-size: 110%;- border-bottom: 1px dotted black;-}--h3 {- font-size: 95%;-}--h4 {- font-size: 90%;- font-style: italic;-}--h5 {- font-size: 90%;- font-style: italic;-}--h1.title {- font-size: 150%;- font-weight: bold;- text-align: left;- border: none;-}--dt code {- font-weight: bold;-}-dd p {- margin-top: 0;-}--a:link {- color: #000099- }--a:visited {- color: #666699- }-a:hover {- color: #666699- }-a:active {- color: #000099- }--#footer {- padding-top: 1em;- font-size: 70%;- color: gray;- text-align: center;-}-
− include/inotify-syscalls.h
@@ -1,61 +0,0 @@-#ifndef _LINUX_INOTIFY_SYSCALLS_H-#define _LINUX_INOTIFY_SYSCALLS_H--#include <sys/syscall.h>--#if defined(__i386__)-# define __NR_inotify_init 291-# define __NR_inotify_add_watch 292-# define __NR_inotify_rm_watch 293-#elif defined(__x86_64__)-# define __NR_inotify_init 253-# define __NR_inotify_add_watch 254-# define __NR_inotify_rm_watch 255-#elif defined(__powerpc__) || defined(__powerpc64__)-# define __NR_inotify_init 275-# define __NR_inotify_add_watch 276-# define __NR_inotify_rm_watch 277-#elif defined (__ia64__)-# define __NR_inotify_init 1277-# define __NR_inotify_add_watch 1278-# define __NR_inotify_rm_watch 1279-#elif defined (__s390__)-# define __NR_inotify_init 284-# define __NR_inotify_add_watch 285-# define __NR_inotify_rm_watch 286-#elif defined (__alpha__)-# define __NR_inotify_init 444-# define __NR_inotify_add_watch 445-# define __NR_inotify_rm_watch 446-#elif defined (__sparc__) || defined (__sparc64__)-# define __NR_inotify_init 151-# define __NR_inotify_add_watch 152-# define __NR_inotify_rm_watch 156-#elif defined (__arm__)-# define __NR_inotify_init 316-# define __NR_inotify_add_watch 317-# define __NR_inotify_rm_watch 318-#elif defined (__sh__)-# define __NR_inotify_init 290-# define __NR_inotify_add_watch 291-# define __NR_inotify_rm_watch 292-#else-# error "Unsupported architecture!"-#endif--static inline int inotify_init (void)-{- return syscall (__NR_inotify_init);-}--static inline int inotify_add_watch (int fd, const char *name, __u32 mask)-{- return syscall (__NR_inotify_add_watch, fd, name, mask);-}--static inline int inotify_rm_watch (int fd, __u32 wd)-{- return syscall (__NR_inotify_rm_watch, fd, wd);-}--#endif /* _LINUX_INOTIFY_SYSCALLS_H */
− include/inotify.h
@@ -1,113 +0,0 @@-/*- * Inode based directory notification for Linux- *- * Copyright (C) 2005 John McCutchan- */--#ifndef _LINUX_INOTIFY_H-#define _LINUX_INOTIFY_H--#include <linux/types.h>--/*- * struct inotify_event - structure read from the inotify device for each event- *- * When you are watching a directory, you will receive the filename for events- * such as IN_CREATE, IN_DELETE, IN_OPEN, IN_CLOSE, ..., relative to the wd.- */-struct inotify_event {- __s32 wd; /* watch descriptor */- __u32 mask; /* watch mask */- __u32 cookie; /* cookie to synchronize two events */- __u32 len; /* length (including nulls) of name */- char name[0]; /* stub for possible name */-};--/* the following are legal, implemented events that user-space can watch for */-#define IN_ACCESS 0x00000001 /* File was accessed */-#define IN_MODIFY 0x00000002 /* File was modified */-#define IN_ATTRIB 0x00000004 /* Metadata changed */-#define IN_CLOSE_WRITE 0x00000008 /* Writtable file was closed */-#define IN_CLOSE_NOWRITE 0x00000010 /* Unwrittable file closed */-#define IN_OPEN 0x00000020 /* File was opened */-#define IN_MOVED_FROM 0x00000040 /* File was moved from X */-#define IN_MOVED_TO 0x00000080 /* File was moved to Y */-#define IN_CREATE 0x00000100 /* Subfile was created */-#define IN_DELETE 0x00000200 /* Subfile was deleted */-#define IN_DELETE_SELF 0x00000400 /* Self was deleted */-#define IN_MOVE_SELF 0x00000800 /* Self was moved */--/* the following are legal events. they are sent as needed to any watch */-#define IN_UNMOUNT 0x00002000 /* Backing fs was unmounted */-#define IN_Q_OVERFLOW 0x00004000 /* Event queued overflowed */-#define IN_IGNORED 0x00008000 /* File was ignored */--/* helper events */-#define IN_CLOSE (IN_CLOSE_WRITE | IN_CLOSE_NOWRITE) /* close */-#define IN_MOVE (IN_MOVED_FROM | IN_MOVED_TO) /* moves */--/* special flags */-#define IN_ONLYDIR 0x01000000 /* only watch the path if it is a directory */-#define IN_DONT_FOLLOW 0x02000000 /* don't follow a sym link */-#define IN_MASK_ADD 0x20000000 /* add to the mask of an already existing watch */-#define IN_ISDIR 0x40000000 /* event occurred against dir */-#define IN_ONESHOT 0x80000000 /* only send event once */--/*- * All of the events - we build the list by hand so that we can add flags in- * the future and not break backward compatibility. Apps will get only the- * events that they originally wanted. Be sure to add new events here!- */-#define IN_ALL_EVENTS (IN_ACCESS | IN_MODIFY | IN_ATTRIB | IN_CLOSE_WRITE | \- IN_CLOSE_NOWRITE | IN_OPEN | IN_MOVED_FROM | \- IN_MOVED_TO | IN_DELETE | IN_CREATE | IN_DELETE_SELF | \- IN_MOVE_SELF)--#ifdef __KERNEL__--#include <linux/dcache.h>-#include <linux/fs.h>-#include <linux/config.h>--#ifdef CONFIG_INOTIFY--extern void inotify_inode_queue_event(struct inode *, __u32, __u32,- const char *);-extern void inotify_dentry_parent_queue_event(struct dentry *, __u32, __u32,- const char *);-extern void inotify_unmount_inodes(struct list_head *);-extern void inotify_inode_is_dead(struct inode *);-extern u32 inotify_get_cookie(void);--#else--static inline void inotify_inode_queue_event(struct inode *inode,- __u32 mask, __u32 cookie,- const char *filename)-{-}--static inline void inotify_dentry_parent_queue_event(struct dentry *dentry,- __u32 mask, __u32 cookie,- const char *filename)-{-}--static inline void inotify_unmount_inodes(struct list_head *list)-{-}--static inline void inotify_inode_is_dead(struct inode *inode)-{-}--static inline u32 inotify_get_cookie(void)-{- return 0;-}--#endif /* CONFIG_INOTIFY */--#endif /* __KERNEL __ */--#endif /* _LINUX_INOTIFY_H */
src/System/INotify.hsc view
@@ -2,7 +2,7 @@ -- | -- Module : System.INotify -- Copyright : (c) Lennart Kolmodin 2006--- License : GPL+-- License : BSD3 -- Maintainer : kolmodin@dtek.chalmers.se -- Stability : experimental -- Portability : hc portable, linux only@@ -21,6 +21,8 @@ module System.INotify ( initINotify+ , killINotify+ , withINotify , addWatch , removeWatch , INotify@@ -30,16 +32,16 @@ , Cookie ) where -#include "inotify.h"+#include "linux/inotify.h" import Prelude hiding (init) import Control.Monad import Control.Concurrent import Control.Concurrent.MVar+import Control.Exception (bracket) import Data.Maybe import Data.Map (Map) import qualified Data.Map as Map-import GHC.Handle import Foreign.C import Foreign.Marshal import Foreign.Ptr@@ -47,7 +49,13 @@ import System.Directory import System.IO import System.IO.Error+#if __GLASGOW_HASKELL__ >= 612+import GHC.IO.Handle.FD (fdToHandle')+import GHC.IO.Device (IODeviceType(Stream))+#else+import GHC.Handle import System.Posix.Internals+#endif import System.INotify.Masks @@ -58,7 +66,7 @@ type EventMap = Map WD (Event -> IO ()) type WDEvent = (WD, Event) -data INotify = INotify Handle FD (MVar EventMap)+data INotify = INotify Handle FD (MVar EventMap) ThreadId ThreadId data WatchDescriptor = WatchDescriptor Handle WD deriving Eq newtype Cookie = Cookie CUInt deriving (Eq,Ord)@@ -151,7 +159,7 @@ deriving Eq instance Show INotify where- show (INotify _ fd _) =+ show (INotify _ fd _ _ _) = showString "<inotify fd=" . shows fd $ ">" @@ -164,19 +172,18 @@ initINotify :: IO INotify initINotify = do fd <- throwErrnoIfMinus1 "initINotify" c_inotify_init- em <- newMVar Map.empty let desc = showString "<inotify handle, fd=" . shows fd $ ">" #if __GLASGOW_HASKELL__ < 608 h <- openFd (fromIntegral fd) (Just Stream) False{-is_socket-} desc ReadMode True{-binary-} #else h <- fdToHandle' (fromIntegral fd) (Just Stream) False{-is_socket-} desc ReadMode True{-binary-} #endif- -- h <- fdToHandle fd- inotify_start_thread h em- return (INotify h fd em)+ em <- newMVar Map.empty+ (tid1, tid2) <- inotify_start_thread h em+ return (INotify h fd em tid1 tid2) addWatch :: INotify -> [EventVariety] -> FilePath -> (Event -> IO ()) -> IO WatchDescriptor-addWatch inotify@(INotify h fd em) masks fp cb = do+addWatch inotify@(INotify h fd em _ _) masks fp cb = do is_dir <- doesDirectoryExist fp when (not is_dir) $ do file_exist <- doesFileExist fp@@ -227,20 +234,20 @@ AllEvents -> inAllEvents removeWatch :: INotify -> WatchDescriptor -> IO ()-removeWatch (INotify _ fd _) (WatchDescriptor _ wd) = do- throwErrnoIfMinus1 "removeWatch" $+removeWatch (INotify _ fd _ _ _) (WatchDescriptor _ wd) = do+ _ <- throwErrnoIfMinus1 "removeWatch" $ c_inotify_rm_watch (fromIntegral fd) wd return () rm_watch :: INotify -> WD -> IO ()-rm_watch (INotify _ _ em) wd =+rm_watch (INotify _ _ em _ _) wd = modifyMVar_ em (return . Map.delete wd) read_events :: Handle -> IO [WDEvent] read_events h = let maxRead = 16385 in allocaBytes maxRead $ \buffer -> do- hWaitForInput h (-1) -- wait forever+ _ <- hWaitForInput h (-1) -- wait forever r <- hGetBufNonBlocking h buffer maxRead read_events' buffer r where@@ -283,12 +290,12 @@ isSet bits = maskIsSet bits mask name = fromJust nameM -inotify_start_thread :: Handle -> MVar EventMap -> IO ()+inotify_start_thread :: Handle -> MVar EventMap -> IO (ThreadId, ThreadId) inotify_start_thread h em = do chan_events <- newChan- forkIO (dispatcher chan_events)- forkIO (start_thread chan_events)- return ()+ tid1 <- forkIO (dispatcher chan_events)+ tid2 <- forkIO (start_thread chan_events)+ return (tid1,tid2) where start_thread :: Chan [WDEvent] -> IO () start_thread chan_events = do@@ -311,6 +318,15 @@ case handlerM of Nothing -> putStrLn "runHandler: couldn't find handler" -- impossible? Just handler -> catch (handler event) (\_ -> return ())++killINotify :: INotify -> IO ()+killINotify (INotify h _ _ tid1 tid2) =+ do killThread tid1+ killThread tid2+ hClose h++withINotify :: (INotify -> IO a) -> IO a+withINotify = bracket initINotify killINotify foreign import ccall unsafe "inotify-syscalls.h inotify_init" c_inotify_init :: IO CInt foreign import ccall unsafe "inotify-syscalls.h inotify_add_watch" c_inotify_add_watch :: CInt -> CString -> CUInt -> IO CInt
src/System/INotify/Masks.hsc view
@@ -31,7 +31,7 @@ import Data.Maybe import Foreign.C.Types -#include "inotify.h"+#include "linux/inotify.h" data Mask = UserSpace CUInt@@ -91,35 +91,3 @@ #enum Mask, Special, IN_ONESHOT #enum Mask, All, IN_ALL_EVENTS--{--/* the following are legal, implemented events that user-space can watch for */-#define IN_ACCESS 0x00000001 /* File was accessed */-#define IN_MODIFY 0x00000002 /* File was modified */-#define IN_ATTRIB 0x00000004 /* Metadata changed */-#define IN_CLOSE_WRITE 0x00000008 /* Writtable file was closed */-#define IN_CLOSE_NOWRITE 0x00000010 /* Unwrittable file closed */-#define IN_OPEN 0x00000020 /* File was opened */-#define IN_MOVED_FROM 0x00000040 /* File was moved from X */-#define IN_MOVED_TO 0x00000080 /* File was moved to Y */-#define IN_CREATE 0x00000100 /* Subfile was created */-#define IN_DELETE 0x00000200 /* Subfile was deleted */-#define IN_DELETE_SELF 0x00000400 /* Self was deleted */-#define IN_MOVE_SELF 0x00000800 /* Self was moved */--/* the following are legal events. they are sent as needed to any watch */-#define IN_UNMOUNT 0x00002000 /* Backing fs was unmounted */-#define IN_Q_OVERFLOW 0x00004000 /* Event queued overflowed */-#define IN_IGNORED 0x00008000 /* File was ignored */--/* helper events */-#define IN_CLOSE (IN_CLOSE_WRITE | IN_CLOSE_NOWRITE) /* close */-#define IN_MOVE (IN_MOVED_FROM | IN_MOVED_TO) /* moves */--/* special flags */-#define IN_ONLYDIR 0x01000000 /* only watch the path if it is a directory */-#define IN_DONT_FOLLOW 0x02000000 /* don't follow a sym link */-#define IN_MASK_ADD 0x20000000 /* add to the mask of an already existing w atch */-#define IN_ISDIR 0x40000000 /* event occurred against dir */-#define IN_ONESHOT 0x80000000 /* only send event once */--}
− tests/Utils.hs
@@ -1,55 +0,0 @@-module Utils where--import Control.Concurrent.Chan-import Control.Exception--import System.Directory-import System.Environment-import System.Exit--import System.INotify--testName = do- n <- getProgName- return (n ++ "-playground")--withTempDir f = do- path <- testName- bracket- ( createDirectory path >> return path )- ( removeDirectoryRecursive )- ( f )--withWatch inot events path action f =- bracket- ( addWatch inot events path action )- ( removeWatch inot )- ( const f )--inTestEnviron events action f = do- withTempDir $ \testPath -> do- inot <- initINotify- chan <- newChan- withWatch inot events testPath (writeChan chan) $ do- action testPath- events <- getChanContents chan- f events--(~=) :: Eq a => [a] -> [a] -> Bool-[] ~= _ = True-(x:xs) ~= (y:ys) = x == y && xs ~= ys-_ ~= _ = False--asMany :: [a] -> [a] -> [a]-asMany xs ys = take (length xs) ys--explainFailure expected reality = do- putStrLn "Expected:"- mapM_ (\x -> putStr "> " >> print x) expected- putStrLn "But got:"- mapM_ (\x -> putStr "< " >> print x) (asMany expected reality)- testFailure--testFailure = exitFailure --testSuccess = exitWith ExitSuccess
− tests/test-all
@@ -1,31 +0,0 @@-#!/bin/bash--GHC=ghc--if [[ -n $1 ]]; then- GHC="$1"- echo Using GHC: $GHC-fi--TESTS=( `ls test*.hs | cut -d. -f1` )--rm -rf *.o *.hi--for t in ${TESTS[@]}; do- rm -rf $t{,.o,.hi}- rm -rf $t-playground- $GHC -v0 --make $t.hs -o $t- echo -n Testing $t ...- if [[ -x $t ]]; then- ./$t- exitCode=$?- if [[ $exitCode == 0 ]]; then- echo Success- rm -rf $t{,.o,.hi}- else- echo Failure with exit code $exitCode- fi- else- echo Compilation failed- fi-done
− tests/test001-list-dir-contents.hs
@@ -1,20 +0,0 @@-module Main where--import Control.Monad--import System.Directory--import System.INotify as INotify--import Utils--main =- inTestEnviron [Open, Close] getDirectoryContents $ \ events -> do- when (expected ~= events)- testSuccess- explainFailure expected events--expected =- [ Opened True Nothing- , Closed True Nothing False- ]
− tests/test002-writefile.hs
@@ -1,24 +0,0 @@-module Main where--import Control.Monad--import System.INotify as INotify--import Utils--write path = do- writeFile (path ++ "/hello") ""- -- actually writing any contents gives me two Modified- -main =- inTestEnviron [AllEvents] write $ \ events -> do- when (expected ~= events)- testSuccess- explainFailure expected events--expected =- [ Created False "hello"- , Opened False (Just "hello")- , Modified False (Just "hello")- , Closed False (Just "hello") True- ]
− tests/test003-removefile.hs
@@ -1,35 +0,0 @@-module Main where--import Control.Monad--import System.Directory--import System.INotify as INotify--import Utils--file = "hello"--write path = do- writeFile (path ++ '/':file) ""--remove path = do- removeFile (path ++ '/':file)--action path = do- write path- remove path- -main =- inTestEnviron [AllEvents] action $ \ events -> do- when (expected ~= events)- testSuccess- explainFailure expected events--expected =- [ Created False file- , Opened False (Just file)- , Modified False (Just file)- , Closed False (Just file) True- , Deleted False file- ]
− tests/test004-modify-file.hs
@@ -1,47 +0,0 @@-module Main where--import Control.Exception-import Control.Monad--import System.Directory-import System.IO--import System.INotify as INotify--import Utils--file = "hello"--write path = do- writeFile (path ++ '/':file) ""--modify path = do- bracket- (openFile (path ++ '/':file) AppendMode)- (hClose)- (\h -> hPutStr h "yarr!")--remove path = do- removeFile (path ++ '/':file)--action path = do- write path- modify path- remove path- -main =- inTestEnviron [AllEvents] action $ \ events -> do- when (expected ~= events)- testSuccess- explainFailure expected events--expected =- [ Created False file- , Opened False (Just file)- , Modified False (Just file)- , Closed False (Just file) True- , Opened False (Just file)- , Modified False (Just file)- , Closed False (Just file) True- , Deleted False file- ]
− tests/test005-move-file.hs
@@ -1,46 +0,0 @@-module Main where--import Data.Maybe--import Control.Monad--import System.Directory-import System.IO--import System.INotify as INotify--import Utils--file = "hello"-file2 = file ++ "2"--write path = do- writeFile (path ++ '/':file) ""--move path = do- renameFile (path ++ '/':file) (path ++ '/':file2)--remove path = do- removeFile (path ++ '/':file2)--action path = do- write path- move path- remove path- -main =- inTestEnviron [AllEvents] action $ \ events -> do- let cookie = head [ c | MovedOut _ _ c <- events ]- when (expected cookie ~= events)- testSuccess- explainFailure (expected cookie) events--expected cookie =- [ Created False file- , Opened False (Just file)- , Modified False (Just file)- , Closed False (Just file) True- , MovedOut False file cookie- , MovedIn False file2 cookie- , Deleted False file2- ]